AI入门教程03.01:全连接神经网络

先来个简单的,写一个全连接神经网络。模型如下图

fc

使用MNIST数据集,内容为手写数字,1通道灰度图。

模型流程为

  • 将1通道28x28图片转换为1通道宽1高784的图片
  • 将784图片缩小为1通道宽1高256图片
  • 将256图片限制为1通道宽1高10图片对应0-9这10个结果

模型开发

将神经网络模型转换为对应代码的基本流程。

硬件选择

根据硬件情况选择加速方式

1
2
3
4
if torch.cuda.is_available():
device = torch.device("cuda") # 如果GPU可用,则使用CUDA
else:
device = torch.device("cpu") # 如果GPU不可用,则使用CPU

加载数据集并进行预处理

缩放、裁剪、归一化数据集中的数据。

MNIST数据集图片大小相同,这一步跳过。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# 加载MNIST数据集的训练集
training_data = datasets.FashionMNIST(
root="data",
train=True,
download=True,
transform=transforms.ToTensor(),
)
# 加载MNIST数据集的测试集
test_data = datasets.FashionMNIST(
root="data",
train=False,
download=True,
transform=transforms.ToTensor(),
)

# batch大小
batch_size = 64

# 创建dataloader
train_dataloader = torch.utils.data.DataLoader(training_data, batch_size=batch_size)
test_dataloader = torch.utils.data.DataLoader(test_data, batch_size=batch_size)

如果指定位置没有数据集就自动下载。

定义模型

根据神经网络模型定义编写代码

1
2
3
4
5
6
7
8
9
10
11
12
13
# 定义神经网络模型
class Net(nn.Module):
def __init__(self):
super(Net, self).__init__()
self.fc1 = nn.Linear(784, 256)
self.fc2 = nn.Linear(256, 10)

def forward(self, x):
x = x.view(-1, 784)
x = self.fc1(x)
x = F.relu(x)
x = self.fc2(x)
return x

全连接使用Linear函数,只要是784->256->10

使用view函数将二维图片转换为一维数组。

全连接后使用激活函数获得数据。

初始化模型、损失函数和优化器

1
2
3
4
5
# 实例化模型并定义损失函数和优化器
net = Net().to(device)
criterion = nn.CrossEntropyLoss() # 交叉熵损失函数适合分类任务
optimizer = torch.optim.SGD(net.parameters(), lr=0.01)
#optimizer = optim.Adam(model.parameters(), lr=learning_rate)

训练

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
for epoch in range(10):
index = 0
for i,inputs, labels in train_dataloader:
inputs, labels = inputs.to(device), labels.to(device)

# 前向传播
outputs = net(inputs)
loss = criterion(outputs, labels)

# 反向传播和优化
optimizer.zero_grad() #梯度归零
loss.backward() #反向传播
optimizer.step() #优化
index+=1

print(f'Epoch [{epoch}], Setp [{index}/{len(train_dataloader)}], Loss: {loss.item():.4f}')

训练时终端会输出当前训练参数,mbox_loss/loss应该是整体下降的,如果是下面的输出是不正常的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
I060515:11:51.84044634915 solver.Cpp:259] Trainnet output #0:mbox_loss =nan(*1 =nan loss)
I060515:11:51.84045134915 sgd_solver.Cpp:138]Iteration 160,lr =0.001
I060515:11:52.56749834915 solver.Cpp:243]Iteration 170,loss =nan
I060515:11:52.56751834915 solver.Cpp:259] Train net output #0:mbox_loss =nan(*1 =nan loss)
I060515:11:52.56752334915 sgd_solver.Cpp:138]Iteration 170,lr =0.001
I060515:11:53.28211634915 solver.Cpp:243]Iteration 180,loss =nan
I060515:11:53.28213834915 solver.Cpp:259] Train net output #0:mbox_loss =nan(*1 =nan loss)
I060515:11:53.28214334915 sgd_solver.Cpp:138]Iteration 180,lr =0.001
I060515:11:54.00279134915 solver.Cpp:243]Iteration 190,loss =nan
I060515:11:54.00284134915 solver.Cpp:259] Trainnet output #0:mbox_loss =nan(*1 =nan loss)
I060515:11:54.00284634915 sgd_solver.Cpp:138]Iteration 190,lr =0.001
I060515:11:54.73202134915 solver.Cpp:243]Iteration 200,loss =nan
I060515:11:54.73204334915 solver.Cpp:259] Trainnet output #0:mbox_loss =nan(*1 =nan loss)
I060515:11:54.73204834915 sgd_solver.Cpp:138]Iteration 200,lr =0.001
I060515:11:55.46026534915 solver.Cpp:243]Iteration 210,loss =nan
I060515:11:55.46028934915 solver.Cpp:259] Trainnet output #0:mbox_loss =nan(*1 =nan loss)
I060515:11:55.46029234915 sgd_solver.Cpp:138]Iteration 210,lr =0.001
I060515:11:56.18789334915 solver.Cpp:243]Iteration 220,loss =nan
I060515:11:56.18791634915 solver.Cpp:259] Trainnet output #0:mbox_loss =nan(*1 =nan loss)
I060515:11:56.18791934915 sgd_solver.Cpp:138]Iteration 220,lr =0.001
I060515:11:56.90772734915 solver.Cpp:243]Iteration 230,loss =nan
I060515:11:56.90774734915 solver.Cpp:259] Trainnet output #0:mbox_loss =0(*1 =0 loss)
I060515:11:56.90775234915 sgd_solver.Cpp:138]Iteration 230,lr =0.001

可以考虑缩小lr值或者batch值

预测

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 加载模型并测试
correct = 0
total = 0
net.eval()
with torch.no_grad():
for inputs, labels in test_dataloader:
inputs, labels = inputs.to(device), labels.to(device)
outputs = net(inputs)
_, predicted = torch.max(outputs.data, 1)
total += labels.size(0)
correct += (predicted == labels).sum().item()

# 结果评估
print('Accuracy: %d %%' % (100 * correct / total))

运行

迭代100次,运行输出为

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
Epoch [99], Setp [917/938],Loss: 0.1756
Epoch [99], Setp [918/938],Loss: 0.2033
Epoch [99], Setp [919/938],Loss: 0.3347
Epoch [99], Setp [920/938],Loss: 0.1615
Epoch [99], Setp [921/938],Loss: 0.1972
Epoch [99], Setp [922/938],Loss: 0.2536
Epoch [99], Setp [923/938],Loss: 0.1282
Epoch [99], Setp [924/938],Loss: 0.1388
Epoch [99], Setp [925/938],Loss: 0.3028
Epoch [99], Setp [926/938],Loss: 0.2694
Epoch [99], Setp [927/938],Loss: 0.4310
Epoch [99], Setp [928/938],Loss: 0.1672
Epoch [99], Setp [929/938],Loss: 0.2218
Epoch [99], Setp [930/938],Loss: 0.2361
Epoch [99], Setp [931/938],Loss: 0.3474
Epoch [99], Setp [932/938],Loss: 0.2533
Epoch [99], Setp [933/938],Loss: 0.4752
Epoch [99], Setp [934/938],Loss: 0.1587
Epoch [99], Setp [935/938],Loss: 0.0987
Epoch [99], Setp [936/938],Loss: 0.2242
Epoch [99], Setp [937/938],Loss: 0.2413
Epoch [99], Setp [938/938],Loss: 0.1720
Accuracy: 88 %

完整代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
import torch
import torch.nn as nn
import torch.nn.functional as F
import torch.optim as optim
from torchvision import datasets, transforms

if torch.cuda.is_available():
device = torch.device("cuda") # 如果GPU可用,则使用CUDA
else:
device = torch.device("cpu") # 如果GPU不可用,则使用CPU

# 定义神经网络模型
class Net(nn.Module):
def __init__(self):
super(Net, self).__init__()
self.fc1 = nn.Linear(784, 256)
self.fc2 = nn.Linear(256, 10)

def forward(self, x):
x = x.view(-1, 784)
x = F.relu(self.fc1(x))
x = self.fc2(x)
return x


# 加载MNIST数据集的训练集
training_data = datasets.FashionMNIST(
root="data",
train=True,
download=True,
transform=transforms.ToTensor(),
)
# 加载MNIST数据集的测试集
test_data = datasets.FashionMNIST(
root="data",
train=False,
download=True,
transform=transforms.ToTensor(),
)

# batch大小
batch_size = 64

# 创建dataloader
train_dataloader = torch.utils.data.DataLoader(training_data, batch_size=batch_size)
test_dataloader = torch.utils.data.DataLoader(test_data, batch_size=batch_size)

# 实例化模型并定义损失函数和优化器
net = Net().to(device)
criterion = nn.CrossEntropyLoss()
optimizer = torch.optim.SGD(net.parameters(), lr=0.01)

# 加载数据并训练
for epoch in range(10):
index=0
for inputs, labels in train_dataloader:
inputs, labels = inputs.to(device), labels.to(device)
optimizer.zero_grad()
outputs = net(inputs)
loss = criterion(outputs, labels)
loss.backward()
optimizer.step()
index+=1
print(f'Epoch [{epoch}], Setp [{index}/{len(train_dataloader)}],Loss: {loss.item():.4f}')

# 加载模型并测试
net.eval()
correct = 0
total = 0
with torch.no_grad():
for inputs, labels in test_dataloader:
inputs, labels = inputs.to(device), labels.to(device)
outputs = net(inputs)
_, predicted = torch.max(outputs.data, 1)
total += labels.size(0)
correct += (predicted == labels).sum().item()

# 结果评估
print('Accuracy: %d %%' % (100 * correct / total))

AI入门教程03.01:全连接神经网络
https://blog.jackeylea.com/ai/how-to-build-a-fully-connected-nn/
作者
JackeyLea
发布于
2024年6月20日
许可协议