当前位置: 首页 > news >正文

[Python] 从0到1实现一个简单的数字图像识别大模型

目录

前言介绍

神经网络

简单的神经网络

使用均方误差与正规方程实现神经网络

随机梯度下降与批量梯度下降实现神经网络

用更复杂的梯度下降实现一个神经网络

利用Sigmoid激活函数实现神经网络

使用 PyTorch 框架快速构建一个神经网络、

案例实战


前言介绍

        大模型的本质是机器学习, 机器学习的本质就是一种数学模型,而现在主流的大模型都是基于神经网络模型构建的数学模型,不论是基于卷积神经网络(CNN),还是循环神经网络(RNN),亦或者是Transformer神经网络等。‍‍‍‍‍‍‍‍所以所谓的大模型,就是一个很复杂的函数,训练它的样本集很大、参数很多。

        神经网络模型是一种基于人工神经元的数学模型,用于模拟人脑的神经网络结构和功能。 神经网络模型有很多层,每一层都有很多个神经元,每一层又是相互连接。每个神经元又由很多参数组成,平时我们常常所说的某个大模型有多少亿参数,就是指所有神经元加起来参数之和。参数越多,大模型的功能就越强大。

        

         一般情况下,大模型的参数是在网络架构时就设定好的,参数数量一般不会发生变化;但也有例外情况,比如动态神经网络就会对参数数量进行动态调整。‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍

        大模型训练的本质就是调整参数,训练的过程其实就是把训练数据输入到大模型中,然后模型根据这些数据对参数进行调整的过程,以求达到一个最优解。因为模型有多个神经层,所以训练数据从输入层进入大模型之后;需要在模型的多个神经层之间进行流转,而这个过程术语叫做正向传播。‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍

        数据从输入层,一层一层的传播到输出层,然后输出结果;但由于大模型刚开始就像一个小学生,所以它输出的结果往往不尽人意。‍‍‍‍‍‍‍‍所以,为了解决这个问题,大模型的输出结果需要跟实际结果进行匹配,术语叫做计算损失差,损失差越大说明输出结果越差。‍‍‍‍‍‍‍‍‍‍‍‍‍

        而有了损失差,说明当前的模型是有问题的;所以就需要对模型进行调整,这就是所谓的反向传播。‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍

神经网络

        大模型是基于神经网络构建的。接下来,我们将通过多种数学模型来实现神经网络的优化。

  • 简单的神经网络

         对于 ypi = w * xi 函数,其实是属于神经网络中的一层,w 是参数,大模型训练调整的就是这个参数。而 xi 和 ypi 在数学中是自变量和因变量,而在神经网络中,它是样本数据,是固定的,也叫训练数据。

        在训练中,不断的根据预测值和真实值,算出他们之间的误差,然后调整 w 的值,误差越小,代码模型预测能力越好。

 训练数据代码

import numpy as npdef get(count):# 生成count个0-1之间随机数xs = np.sort(np.random.rand(count))ys = []for x in xs:y = 1.5 * x + np.random.rand() / 10ys.append(y)ys = np.array(ys)# 变为负数# xs = xs * -1# ys = ys * -1return xs, ys

 神经网络代码

import matplotlib.pyplot as plotimport housexs, ys = house.get(100)
w = 0.5
for i in range(100):xi = xs[i]yi = ys[i]ypi = w * xi  # 预测值  正向传播e = yi - ypi  # 误差w = w + e * xi  # 根据误差修改w  反向传播# 以下只是为了画出新预测函数对应的直线yps = w * xs  # 新的预测函数plot.clf()  # 清空重新绘制,这样就能看到动态效果plot.xlim(0, 1) #设置了 x 轴的显示范围plot.ylim(0, 1.6) #设置了 y 轴的显示范围plot.scatter(xs, ys) #绘制了一个散点图plot.plot(xs, yps) #绘制了一条折线图plot.pause(0.1) #暂停图形的显示 0.1 秒plot.show() #显示当前绘制的图形

运行结果如下:

        通过运行结果可以看出,函数 ypi = w * xi 的直线不断的靠近散点样本数据,代表误差越来越小,预测能力也越来越好。

  •  使用均方误差与正规方程实现神经网络

           上面例子实现的简单的神经网络,是根据预测值和真实值,算出他们之间的误差,进而调整参数值,但受样本的数据影响很大,如果最后一个样本数据存在问题,就会导致整个训练过程报废。

        针对上面出现的问题,就可以使用均方误差和正规方程来解决,所谓均方误差,就是把所有的误差相加,再除以样本数。

        单个样本的误差计算方式为:(真实值-预测值​)²,用函数来表示就是 e = ( yi − w⋅xi )²,有人会很奇怪,误差为什么要加平方,其实是为了消除负值,那误差不是变大了?其实没关系,因为我们关注的是 w 这个参数值。

 将误差公式展开为:

这样,对于某个样本(xi,yi)而言,就可以通过上述公式来计算误差e了,我们稍微调整一下位置:

通过上述公式,可以看出,如果我们把w看成自变量,e看成因变量,xi和yi看成常量,那么其实就是一个一元二次函数,对应的就是一条抛物线。

而针对所有样本,我们要计算全部样本的整体误差,我们只需要计算所有样本的误差e,然后累计e,再除以样本个数就得到了全体样本的均方误差

        拆开后:

所以,针对全体样本的均方误差也是一个一元二次方程,也是一个抛物线,而且是开口向上的抛物线,所以抛物线的最低点就表示误差最小的点,而根据抛物线的最地方求解公式就能得到 。

而对应的抛物线的顶点就是所有样本的评价误差最小值。而根据抛物线的顶点坐标公式:

而w是自变量,所以最小w值为 -b/(2a),也就是

约分之后就是:

 而这就是正规方程。通过它就能直接得到误差e最小时的w值。

代码:

import matplotlib.pyplot as plotimport housexs, ys = house.get(100)
ys[99] = 2w = 0.5sum_xy = 0
sum_xx = 0
for i in range(100):xi = xs[i]yi = ys[i]sum_xy += xi * yisum_xx += xi * xiw = sum_xy / sum_xxyps = w * xsplot.clf()
plot.xlim(0, 1)
plot.ylim(0, 1.6)
plot.scatter(xs, ys)
plot.plot(xs, yps)plot.show()

  • 随机梯度下降与批量梯度下降实现神经网络

          使用均方误差与正规方程实现神经网络,虽然能很好解决因某个样本而导致训练的不准确,但需要对样本数据进行累加,如果样本量太多,就会过多占用服务器CPU,内存等资源,接下来使用梯度下降来解决这个问题。

        还是看误差抛物线,我们不直接取 w 的最低点计算,而且取 w 点的斜率 来不断地调整 w 值。让 w 值不断趋向于最低点。

              1.  抛物线上某个点的斜率如果小于0,那么表示在右边。

              2. 抛物线上某个点的斜率如果大于0,那么表示在左边。

              3. 抛物线上某个点的斜率如果等于0,那么表示在最低点。

误差抛物线图

那么某点的斜率该如何得出呢?实际上就是求导。

求导

假设抛物线上存在某点(w,e),那么该点的斜率为:

这样,我们就能知道某个w对应的斜率了。

其中:

对于给定的样本集而言,a,b的值是固定的,所以我们只需要不断调整 w 的值就可以了。对于 a 和 b 为什么等于上面两个值,大家可以上面 均方误差与正规方程那里。

那如何调整 w 的值呢?

        像我们上面所说的,抛物线上某一点的斜率小于 0 的话(参考上面误差抛物线图),那边代表在右边,这时我们只要增加 w 的值就可以,那增加多少呢?这时我们可以自己设置一个大小,步长越大,调整的越快,但是精确度越低,步长越小,调整的越慢,但是精确度越高。这个在深度学习中叫步长。调整公式如下:

w新 = w旧 - 步长 * 斜率

如果某一点上的斜率大于 0 的话,则相反,减小 w 的值就行。

代码实战 - 随机梯度下降

import matplotlib.pyplot as plotimport housexs, ys = house.get(100)alpha = 0.1  # 学习率,步长
w = 0.1
for i in range(100):xi = xs[i]yi = ys[i]# 斜率k = 2 * (xi ** 2) * w + (-2 * xi * yi)w = w - alpha * k  # k大于0,要减少w,k小于0,要增加wyps = w * xs  # 新的预测函数plot.clf()  # 清空重新绘制,这样就能看到动态效果plot.xlim(0, 1)plot.ylim(0, 1.6)plot.scatter(xs, ys)plot.plot(xs, yps)plot.pause(0.01)plot.show()

上面代码一个一个的取样本的数据,如果你觉得慢,想批量的 取,可以使用mini批量梯度下降。

代码实战 - mini批量梯度下降

import matplotlib.pyplot as plot
import numpyimport housexs, ys = house.get(100)w = 0.1
alpha = 0.1  # 学习率,步长
for _ in range(50):# 每次取10个样本for i in range(0, 100, 10):xsi = xs[i::i + 10]ysi = ys[i::i + 10]a = numpy.sum(xsi ** 2) / 10b = -2 * numpy.sum(xsi * ysi) / 10k = 2 * a * w + b  # k表示w点的斜率w = w - alpha * k  # k大于0,要减少w,k小于0,要增加wyps = w * xs  # 新的预测函数plot.clf()  # 清空重新绘制,这样就能看到动态效果plot.xlim(0, 1)plot.ylim(0, 1.6)plot.scatter(xs, ys)plot.plot(xs, yps)plot.pause(0.01)plot.show()

  • 用更复杂的梯度下降实现一个神经网络

        如果样本数据这样的话,我们该如何设计的神经网络?

前面我们设计的函数都是y = wx,而忽略了 b ,而对于上面的样本,我们就需要用到 b 这个参数。

w 决定了直线的倾斜,而 b 决定了 直线的高度。

那我们该如何设计,使得函数直线更贴近样本数据呢?

        我们还是可以使用梯度下降来解决,通过不断地调整 w 和 b 的值,找到整体误差最小的 w 和 b的值,均方误差公式:

将误差公式展开调整为:

我们可以把样本和 b 都看成常量,e 和 w 之间还是一条抛物线。

因为我们不仅需要调整 w 的值,还需要调整 b 的值, 此时我们需要同时对 w 和 b进行求导。然后进行梯度下降。

e 对 w 的导数:

e 对 b 的导数:

代码实战 :

import numpy as npdef get(count):# 生成count个0-1之间随机数xs = np.sort(np.random.rand(count))ys = []for x in xs:y = 1.5 * x + np.random.rand() / 10ys.append(y)ys = np.array(ys)# 变为负数# xs = xs * -1# ys = ys * -1# 倒置xs = np.flip(xs)return xs, ys
import matplotlib.pyplot as plot
import housexs, ys = house.get(100)plot.plot(xs, ys)w = 0.1
b = 0.1
alpha = 0.1  # 学习率,步长# 15表示epoch
for _ in range(15):for i in range(100):xi = xs[i]yi = ys[i]dw = 2 * (xi ** 2) * w + 2 * xi * (b - yi)db = 2 * b - 2 * (yi - w * xi)w = w - alpha * dwb = b - alpha * dbyps = w * xs + b  # 新的预测函数plot.clf()  # 清空重新绘制,这样就能看到动态效果plot.xlim(0, 1)plot.ylim(0, 1.6)plot.scatter(xs, ys)plot.plot(xs, yps)plot.pause(0.01)plot.show()

运行结果如下:

  •  利用Sigmoid激活函数实现神经网络

        如果样本又是怎么样的呢?

 像这种样本数据是这种分散的, 我们再用直接来设计我们神经网络是不行的,这时我们就需要曲线。

 我们可以直接用激活函数,常见的激活函数有:

 而我们上面的样本数据,可以用 Sigmoid 激活函数,Sigmoid 函数的公式是

 Sigmoid 函数图如下, 竟然是一条直线,真是让人惊呆了。不是说好了曲线吗?竟然不按套路出牌,这其实是 Sigmoid 函数很受样本数据的影响,对 x 的取值范围是有要求的,如果样本集太小的话,就像下面那样,展示出来是一条直线。

 

 那如何解决这个问题?

我们可以结合线性函数和sigmoid函数来解决这个问题:

  1. 保持样本集不变,将样本输入线性函数,得到线性函数的结果y
  2. 再把线性函数的输出结果,输入到 sigmoid 函数,得到 sigmoid 函数的结果 z
  3. 判断 z 和样本真实值之间的误差 e,通过不断调整线性函数的 w 和 b,使得误差 e 越来越小

在这个过程中,就能通过不断调整w和b的值,使得原本的样本x的值,经过线性函数后,得出的结果能符合sigmoid的范围。

线性函数为:

 Sigmoid 函数为:

 线性函数的值,需要传给 Sigmoid 函数,则预测函数为

 我们还是使用梯度下降的方式来找到误差最小的点,来调整 w 和 b 的值,也就是求 导。

均方误差:

 对于这种复杂函数的求导,我们可以做拆分

那么:

  • e对w的导数,为e对k的导数*k对t的导数*t对w的导数
  • e对b的导数,为e对k的导数*k对t的导数*t对b的导数

e对k的导数为:

k对t的导数为:

t对w的导数为:

t对b的导数为:

代码实战:

import numpy as npdef get(counts):xs = np.sort(np.random.rand(counts))ys = []for i in range(counts):if i < counts / 2:ys.append(0)  else:ys.append(1) ys = np.array(ys)return xs, ys
import matplotlib.pyplot as plot
import numpyimport housexs, ys = house.get(100)w = 0.1
b = 0.1for j in range(1000):for i in range(len(xs)):xi = xs[i]yi = ys[i]t = w * xi + bk = 1 / (1 + numpy.exp(-t))e = (yi - k) ** 2dedk = -2 * (yi - k)dkdt = k * (1 - k)dtdw = xidtdb = 1dedw = dedk * dkdt * dtdwdedb = dedk * dkdt * dtdbalpha = 0.1  # 学习率,步长w = w - alpha * dedwb = b - alpha * dedbif (j % 100 == 0):yps = 1 / (1 + numpy.exp(-(w * xs + b)))  # 新的预测函数plot.clf()  # 清空重新绘制,这样就能看到动态效果plot.xlim(0, 1)plot.ylim(-0.2, 1.2)plot.scatter(xs, ys)plot.plot(xs, yps)plot.pause(0.01)plot.show()

运行结果如下:

  •  使用 PyTorch 框架快速构建一个神经网络、

      上面创建神经网络的流程都好复杂,可以直接使用 PyTorch 框架,快速构建一个神经网络 , PyTorch 是 Python 语言开发的深度学习框架,专门针对 GPU加速的深度神经网络编程。

Github 地址:https://github.com/pytorch/pytorch

官网:https://pytorch.org/

论坛:https://discuss.pytorch.org/

代码实战:

import matplotlib.pyplot as plt
import numpy as np
import torch
import torch.nn as nn
import torch.optim as optim
from torch.utils.data import Dataset, DataLoader# 数据
xs = np.array([0.00335400, 0.01282081, 0.03225714, 0.0418231, 0.06224218, 0.06963194,0.08320899, 0.08896305, 0.10781348, 0.12513706, 0.12787409, 0.15146274,0.16579344, 0.17624051, 0.18054, 0.19480951, 0.29320166, 0.29657286,0.31641167, 0.32839255, 0.33380987, 0.34495281, 0.37497198, 0.39868539,0.44614808, 0.46220023, 0.48874932, 0.51609881, 0.52824768, 0.54047126,0.54360899, 0.54395718, 0.55205861, 0.56033965, 0.57219896, 0.65985412,0.66736228, 0.67859612, 0.68135888, 0.68427166, 0.68967927, 0.73490296,0.76824992, 0.77612128, 0.79795515, 0.84028841, 0.90513846, 0.92928611,0.93766008, 0.9655463])
ys = np.array([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1,1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1,1, 1])class CustomDataset(Dataset):def __init__(self, xs, ys):self.xs = torch.tensor(xs, dtype=torch.float32).view(-1, 1)self.ys = torch.tensor(ys, dtype=torch.float32).view(-1, 1)def __len__(self):return len(self.xs)def __getitem__(self, idx):return self.xs[idx], self.ys[idx]# 模型定义
class SimpleModel(nn.Module):def __init__(self):super(SimpleModel, self).__init__()self.layer1 = nn.Linear(1, 3)self.layer2 = nn.Linear(3, 1)self.sigmoid = nn.Sigmoid()def forward(self, x):x = self.sigmoid(self.layer1(x))x = self.sigmoid(self.layer2(x))return xdataset = CustomDataset(xs, ys)
batch_size = 8
dataloader = DataLoader(dataset, batch_size=batch_size, shuffle=True)model = SimpleModel()# 损失函数和优化器
criterion = nn.MSELoss()
optimizer = optim.SGD(model.parameters(), lr=1.0)# 训练模型
epochs = 20000
for epoch in range(epochs):for batch_xs, batch_ys in dataloader:model.train()optimizer.zero_grad()outputs = model(batch_xs)loss = criterion(outputs, batch_ys)loss.backward()optimizer.step()if (epoch + 1) % 5000 == 0:print(f'Epoch [{epoch + 1}/{epochs}], Loss: {loss.item()}')# 预测
model.eval()
with torch.no_grad():predictions = model(torch.tensor(xs, dtype=torch.float32).view(-1, 1)).numpy()# 绘图
plt.scatter(xs, ys)
plt.plot(xs, predictions)
plt.show()

运行结果如下:

案例实战

自定义神经网络实现手写体数字图像识别

# 定义模型
import torch
from torch import nnclass Net(nn.Module):def __init__(self):super(Net, self).__init__()self.fc1 = nn.Linear(784, 200)self.fc2 = nn.Linear(200, 200)self.fc3 = nn.Linear(200, 200)self.fc4 = nn.Linear(200, 10)def forward(self, x):x = x.view(-1, 784)  # 展平图像为一维向量x = torch.relu(self.fc1(x))x = torch.relu(self.fc2(x))x = torch.relu(self.fc3(x))x = self.fc4(x)return x
import torch
import torch.nn as nn
import torch.optim as optim
from torch.nn.functional import one_hot
from torch.utils.data import DataLoader
from torchvision import datasets, transformsfrom net import Netdevice = torch.device('cpu')# 定义转换操作
transform = transforms.Compose([transforms.ToTensor()
])# 加载MNIST数据集
train_dataset = datasets.MNIST(root='./data', train=True, download=True, transform=transform)
test_dataset = datasets.MNIST(root='./data', train=False, download=True, transform=transform)# 创建数据加载器
train_loader = DataLoader(dataset=train_dataset, batch_size=6000, shuffle=True)
test_loader = DataLoader(dataset=test_dataset, batch_size=1000, shuffle=False)# 实例化模型
model = Net()
model = model.to(device)# 定义损失函数和优化器
criterion = nn.MSELoss()
optimizer = optim.SGD(model.parameters(), lr=1)# 训练模型
for epoch in range(200):for i, (images, labels) in enumerate(train_loader):images = images.to(device)labels = labels.to(device)# 转换标签为one-hot编码labels = one_hot(labels, num_classes=10).float()# 前向传播outputs = model(images)loss = criterion(outputs, labels)# 反向传播和优化optimizer.zero_grad()loss.backward()optimizer.step()print(f'Epoch [{epoch+1}/200], Loss: {loss.item():.4f}')# 保存模型
torch.save(model.state_dict(), "best.pt")
import torch
from PIL import Image
from torchvision import transformsfrom net import Netdevice = torch.device('cpu')model = Net()
model = model.to(device)# 加载训练好的模型权重
model.load_state_dict(torch.load("best.pt"))# 定义转换操作
transform = transforms.Compose([transforms.ToTensor()
])def load_image(image_path):image = Image.open(image_path).convert('L')  # 转换为灰度图像image = transform(image)image = image.unsqueeze(0)  # 添加批次维度return image# 从https://huggingface.co/datasets/ylecun/mnist?image-viewer=image-0-5F2BE6C77CB0003C82CE3E8D89EDAE0F36E709EC下载图片到项目中
image_path = './img.png'image = load_image(image_path)
image = image.to(device)model.eval()
with torch.no_grad():outputs = model(image)_, predicted = torch.max(outputs.data, 1)print(f'Predicted label: {predicted.item()}')

 运行结果如下:

Predicted label: 5

相关文章:

  • H5漂流瓶社交系统源码
  • 肖扬老师好书《微权力下的项目管理(第3版)》读书笔记1
  • kubelet 探针
  • 14.1 为什么说k8s中监控更复杂了
  • 营养作用的对象是有区别的 第八篇
  • 2025年25届新文出炉:如何打造Java SpringBoot Vue个性化课程推荐系统?
  • UMI复现基础环境安装配置全流程(三)——UMI环境搭建
  • 基于javaweb的茶园茶农文化交流平台的设计与实现(源码+L文+ppt)
  • JVM 调优篇1 类的加载器与加载过程
  • 老古董Lisp实用主义入门教程(8):挠痒痒先生建网站记
  • C#通过ACE OLEDB驱动程序访问 Access和 Excel
  • 逻辑代数的基本规则
  • (Java入门)学生管理系统
  • 记忆化搜索【下】
  • 【论文阅读】CiteTracker: Correlating Image and Text for Visual Tracking
  • 78. Subsets
  • Codepen 每日精选(2018-3-25)
  • Computed property XXX was assigned to but it has no setter
  • Java 11 发布计划来了,已确定 3个 新特性!!
  • JS基础篇--通过JS生成由字母与数字组合的随机字符串
  • Js基础知识(一) - 变量
  • leetcode-27. Remove Element
  • node和express搭建代理服务器(源码)
  • Redis提升并发能力 | 从0开始构建SpringCloud微服务(2)
  • Spark in action on Kubernetes - Playground搭建与架构浅析
  • tweak 支持第三方库
  • Vue小说阅读器(仿追书神器)
  • 从重复到重用
  • 翻译--Thinking in React
  • 浮现式设计
  • 紧急通知:《观止-微软》请在经管柜购买!
  • 前端代码风格自动化系列(二)之Commitlint
  • 前端学习笔记之观察者模式
  • 人脸识别最新开发经验demo
  • 我的面试准备过程--容器(更新中)
  • 移动互联网+智能运营体系搭建=你家有金矿啊!
  • 用Visual Studio开发以太坊智能合约
  • 在Unity中实现一个简单的消息管理器
  • 资深实践篇 | 基于Kubernetes 1.61的Kubernetes Scheduler 调度详解 ...
  • ​VRRP 虚拟路由冗余协议(华为)
  • (3)STL算法之搜索
  • (C++17) optional的使用
  • (k8s)Kubernetes本地存储接入
  • (Redis使用系列) Springboot 使用Redis+Session实现Session共享 ,简单的单点登录 五
  • (vue)el-tabs选中最后一项后更新数据后无法展开
  • (windows2012共享文件夹和防火墙设置
  • (八十八)VFL语言初步 - 实现布局
  • (分享)自己整理的一些简单awk实用语句
  • (附源码)springboot掌上博客系统 毕业设计063131
  • (一)python发送HTTP 请求的两种方式(get和post )
  • (一)认识微服务
  • (原創) 如何將struct塞進vector? (C/C++) (STL)
  • (转)Linq学习笔记
  • .htaccess 强制https 单独排除某个目录
  • .Net 高效开发之不可错过的实用工具