神经网络长啥样?有没有四只眼睛八条腿?
借图镇楼 -
真的是非常经典,可以给下面的解释省掉很多力气。
分3个维度阐述 -
1)输入数据集。假如你自己去微调一下大模型就知道,最开始的一步就是要准备(足够大的)数据集,比如百度就要求1kw条+的数据集,否则就不给你训练。PyTorch官方的数据集是用的著名的FashionMNIST,这是一个由许多28pixel * 28pixel的黑白像素组成的图片(img),类似于windows中的缩略图,每一个图片有标记(label),以及序号(idx)。
感兴趣的您也可以自行下载,下载来的是压缩格式.gz,里面图片是用PIL格式存储的单一二进制文件。如果想看看这些黑白缩略图长啥样呢,可以下载和渲染看看 -
python
import torch
from torch.utils.data import Dataset
from torchvision import datasets
from torchvision.transforms import ToTensor
import matplotlib.pyplot as plt
training_data = datasets.FashionMNIST(
root="drive/MyDrive/",
train=True,
download=True,
transform=ToTensor()
)
test_data = datasets.FashionMNIST(
root="drive/MyDrive/",
train=False,
download=True,
transform=ToTensor()
)
python
labels_map = {
0: "T-Shirt",
1: "Trouser",
2: "Pullover",
3: "Dress",
4: "Coat",
5: "Sandal",
6: "Shirt",
7: "Sneaker",
8: "Bag",
9: "Ankle Boot",
}
figure = plt.figure(figsize=(8, 8))
cols, rows = 3, 3
for i in range(1, cols * rows + 1):
sample_idx = torch.randint(len(training_data), size=(1,)).item()
print(sample_idx)
img, label = training_data[sample_idx]
figure.add_subplot(rows, cols, i)
plt.title(labels_map[label])
plt.axis("off")
plt.imshow(img.squeeze(), cmap="gray")
plt.show()
2)输入有了。对于以上数据集,那就是一大堆784的数组或者列表。你需要把这个转化为1x784的tensor张量或者矢量或者向量,被称为flatten层,也就是把所有信息一维化或者叫扁平化。tensor这个词我们回头专门讨论一下,现在理解为多维数组就好。
那接下来整几层网络?分别干啥用?每一层多少个神经元?最后的输出咋整?
python
import os
import torch
from torch import nn
from torch.utils.data import DataLoader
from torchvision import datasets, transforms
class NeuralNetwork(nn.Module):
def __init__(self):
super().__init__()
self.flatten = nn.Flatten()
self.linear_relu_stack = nn.Sequential(
nn.Linear(28*28, 512),
nn.ReLU(),
nn.Linear(512, 512),
nn.ReLU(),
nn.Linear(512, 10),
)
def forward(self, x):
x = self.flatten(x)
logits = self.linear_relu_stack(x)
return logits
y=nn.Linear(28*28, 512)
print(y,"\n")
z=nn.ReLU(y)
print(z,"\n")
以上是官方的代码,初始化第二层用了512,这个用来存储输出的特征数量,那么784个数用512个特征是否足够表达呢?会不会欠拟合or过拟合呢?这个就完全是经验值了,或者说,要根据你特定的场景或者模型来跑跑看了。反正本例中,每个输入图像通过这个层后,会被转换成一个包含512个数值的向量。中间的隐藏层,是为了方便把输入过渡到输出,你把数字换成1024也完全没有毛病(有可能你的显卡会对你提出抗议,给你穿个小鞋啥的)
本例的输出是为了统计图像的类别,类别一共有10类,从0-T-shirt到9-踝靴,所以输出用10位来表达足够,这里采用了一种被成为one-hot编码的常用的数据表示方法,One-hot编码将分类数据表示为二进制(0和1)的格式,其中每个类别由一个唯一的二进制向量表示,该向量的长度等于类别的总数。例如,假设我们有一个包含三个可能类别的数据集:A、B和C。我们可以用以下one-hot编码表示它们:
- 类别 A: [1, 0, 0]
- 类别 B: [0, 1, 0]
- 类别 C: [0, 0, 1]
3)综上,我们根据输入输出的形式,设计了一个4层(线性化+输入层,隐藏中间层1,隐藏中间层2,输出层)。现在问题来了,这个稚嫩的神经网络怎么干活呢?它怎么不断的学习和奖惩,从而准确的看到个图就报出类别名呢?
因为用了torch库,所以一些细节在代码中被隐藏了。
比如,为了把784个像素和512个特征值对应起来,其实隐含生成了一个权重矩阵和偏置向量。权重是一个矩阵,其行数等于输出特征数(512),列数等于输入特征数(784)。偏置是一个长度为输出特征数(512)的向量。
这个就和上一章节中我们讨论过的玩游戏的AI的脑子对应上了。我们需要初始化权重矩阵(全0或者全1),和偏置向量(从0或者1开始)。然后使用贪婪算法和梯度下降算法,不断的迭代优化。
呃,还漏了一点,激活函数ReLU(),这个是为了让我们的初始化和迭代过程更加平滑一点,避免0或者1产生一些诸如"死亡神经元"之类的算法缺陷(即当输入为负时,梯度为零,导致部分神经元停止更新。)。激活函数有很多种,默认的是f(x) = max(0, x),也就是在x大于0时直接输出x,小于0时输出0。它简单且计算效率高,是当前最流行的激活函数之一,尤其适用于隐藏层。其实可以选择的激活函数还有很多,比如Tanh,Exponential,Sigmoid等等。