用 PyTorch 构建和训练一个基本的神经网络(二)
在用 PyTorch 构建和训练一个基本的神经网络(一)中,我们展示了如何快速构建和训练一个简单的神经网络。其中有很多概念对于初学者来说没见过,这篇文章就来具体解释一下。
torch.randn
和 数据生成
X = torch.randn(100, 3)
y = torch.randn(100, 1)
torch.randn
函数用于生成具有标准正态分布(均值为0,方差为1)的随机数张量。在这里,我们生成了 100 个样本,每个样本有 3 个特征。目标值 y
同样是 100 个样本,但每个样本只有 1 个输出。
举个例子:假如你想做个模型来对图片进行分类。那么需要一些数据来训练模型(不训练就会,那是神仙,不是神经网络)。你得告诉你的模型,这个是猫,那个是狗,等等。。。
注意,这个是猫中的这个
,是什么? 你可能指着一只猫,或者一只狗,此时“这个”就代表这个具体的生物,是你的眼睛看到的一幅图像。
对于计算机来说,你得告诉它你看到的图像。这可以通过摄像头,照相机等拍摄的图片,传递给计算机来达成。
再往深一步想,计算机是怎样看到图片的?图片在计算机的眼里,只是一堆数据而已。那么你想训练他,就得给它这些数据。
Pytorch接收到图片后(这货其实只接收 tensor, 图片要以特定方式转换为 tensor),会通过一系列的算法进行对图像进行特征提取,然后根据提取出来的特征进行分析判断,最后得出一个结论:这货有 90% 的可能性是条狗!
那么问题来了,它怎么能有这个结论呢,答案就是它经过训练了。我们给了它一个图像,它说:这是猫吧?我们告诉它,错,这是条狗。它说:哦,原来这是狗,学习了学习了。。。把此类特征加到“狗”类别。
我们再给它一个图像,它说:这是狗,我们告诉它,这是猫啊!它说:哦,原来是猫,学习了学习了。。。把此类特征加到 “猫”类别。
经过不断的训练,它见识到了很多的猫狗,并在内心中总结了大量特征,终于它成熟起来,能辨别猫狗了。但是注意:它并不能保证准确率为100%。这也是目前神经网络的一大缺憾,它不是神,是神经(网络)。训练数据总是有限的,而这个宇宙却有无限可能。如果一只狗穿上西装坐在椅子上,它很可能觉得不是狗。
扯远了。我们需要训练数据来进行训练。那么 X 代表100个样本,每个样本有3个特征,就好比: X 是一百张动物的图片,每张图片都有三个特征:脑袋形状,尾巴形状,嘴巴形状。 这三个特征,对应 y 中的一个数据->结论, 比如: “猫” 或者 “狗”。但是这些数据都是以数字的形式表示的,而不是文字形式。
训练就是让模型通过三个特征,计算之后,得出一个结论: “猫”或者“狗”. 如果计算结论和 y 中对应的答案不匹配,就是错了,损失函数就要进行修正(大概就是这个意思)
刚开始训练时,模型肯定是瞎猜。但是随着训练次数的增加,它会总结出一些特征,慢慢的变得聪明,最终可以非常准确的识别。
TensorDataset
和 DataLoader
dataset = TensorDataset(X, y)
dataloader = DataLoader(dataset, batch_size=10, shuffle=True)
TensorDataset
: 将输入 X
和标签 y
打包成一个数据集,方便之后通过 DataLoader
进行批次(batch)加载。
DataLoader
: 用于加载数据集,可以设置 batch_size
来定义每次加载多少数据,并且通过 shuffle=True
参数来随机打乱数据。这样做是为了避免模型学习到数据的顺序性,从而提高泛化能力。
定义简单的神经网络
class SimpleNN(nn.Module):
def __init__(self):
super(SimpleNN, self).__init__()
self.fc1 = nn.Linear(3, 5)
self.fc2 = nn.Linear(5, 1)
def forward(self, x):
x = torch.relu(self.fc1(x))
x = self.fc2(x)
return x
nn.Module
:nn.Module
是所有神经网络模块的基类。你自己定义的每一个神经网络模块都应继承这个类,并实现__init__
和forward
方法。nn.Linear
: 这是一个全连接层,也叫做线性层。fc1
将输入维度从 3 转换为 5,fc2
将 5 转换为 1。forward
方法: 在这个方法中定义了网络的前向传播过程。每次传入数据x
,先经过第一层fc1
,然后通过 ReLU 激活函数,再传递到第二层fc2
。- ReLU 激活函数什么鬼?目前咱只要知道:它把负数变成0,正数不变。这有什么用?你以后再操心好吧,路漫漫其修远兮。。。
损失函数和优化器
criterion = nn.MSELoss()
optimizer = optim.SGD(model.parameters(), lr=0.01)
nn.MSELoss
: 这是一个均方误差损失函数,用于回归问题。在训练中,我们会计算预测值和真实值之间的均方误差,并使用这个误差来指导模型参数的调整。optim.SGD
: 这是一个优化器,使用随机梯度下降法更新模型参数。model.parameters()
获取了模型中所有可训练的参数。
这个法那个法的,你了解一下便好,随便上网一查,知道各种意味即可。日后可慢慢研究。。。
训练模型
for epoch in range(100):
for batch_X, batch_y in dataloader:
optimizer.zero_grad()
output = model(batch_X)
loss = criterion(output, batch_y)
loss.backward()
optimizer.step()
optimizer.zero_grad()
: 每次训练前都需要将梯度清零,否则梯度会累积。model(batch_X)
: 调用模型的forward
方法,进行前向传播,得到预测输出。loss.backward()
: 反向传播,计算梯度。这一步将基于损失值计算每个参数的梯度。optimizer.step()
: 使用优化器更新模型参数,进行一次梯度下降。
测试模型
with torch.no_grad():
test_X = torch.tensor([[0.5, -0.2, 0.1]])
test_output = model(test_X)
print(f'Test Output: {test_output.item()}')
torch.no_grad()
: 在测试或验证模型时,不需要计算梯度,所以使用torch.no_grad()
上下文管理器来关闭梯度计算。这不仅节省内存,还加快了计算速度。model(test_X)
: 对测试数据进行预测。test_output.item()
:item()
方法将张量中的值提取出来,作为一个标准 Python 数值。
不要想一口气吃个胖子,一下子什么都搞懂。相对来说,每天只问两个问题,一个月下来,你就懂了60个问题。岂不轻松哉…