import random
import torch
from torch import nn, optim
import math
from IPython import display
from plot_lib import plot_data, plot_model, set_default
device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
print('device:', device)
# 设置随机种子
seed = 35
random.seed(seed)
torch.manual_seed(seed)
N = 500 # 每类样本的数量
D = 2 # 每个样本的特征维度
C = 5 # 样本的类别
H = 100 # 神经网络里隐藏层单元的数量
X = torch.zeros(N * C, D).to(device)
Y = torch.zeros(N * C, dtype=torch.long).to(device)
for c in range(C):
index = 0
t = torch.linspace(0, 1, N)
inner_var = torch.linspace((2*math.pi/C)*c, (2*math.pi/C)*(2+c), N) + torch.randn(N)*0.2
for ix in range(N*c, N*(c+1)):
X[ix] = t[index] * torch.FloatTensor((math.sin(inner_var[index]), math.cos(inner_var[index])))
Y[ix] = c
index += 1
print("Shapes:")
print("X:", X.size())
print("Y:", Y.size())
```
Shapes:
X: torch.Size([2500, 2])
Y: torch.Size([2500])
```
设置每类样本的数量,样本的类别,隐藏层单元的数量等参数,根据样本数量和类别量生成螺旋样本,每个样本是(X,Y)样本的坐标保存在X中,而类别信息在Y中。
learning_rate = 1e-3 # 学习率
lambda_l2 = 1e-5 # 权重衰减
# 两层全连接层
model = nn.Sequential(
nn.Linear(D, H),
nn.Linear(H, C)
)
model.to(device)
# 交叉熵损失函数
criterion = torch.nn.CrossEntropyLoss()
# 优化器
optimizer = torch.optim.SGD(model.parameters(), lr=learning_rate, weight_decay=lambda_l2)
for i in range(10000):
y_pred = model(X) # 数据输入模型
loss = criterion(y_pred, Y) # 计算损失
score, predicted = torch.max(y_pred, 1) # 得到预测结果
acc = (Y == predicted).sum().float() / len(Y) # 判断Y(标签)是否和预测结果相等,计算准确率
display.clear_output(wait=True)
optimizer.zero_grad() # 梯度置0
loss.backward() # 反向传播
optimizer.step()
学习率:也即步长,是误差反向传播中的一个参数。用于控制每一次更新参数利用多少误差。
学习率越大,输出误差对参数的影响就越大,参数更新的就越快,但同时受到异常数据的影响就越大,容易在极值附近震荡。
学习率过小又会导致收敛速度较慢,且容易过拟合。
得到的预测结果
两层全连接层仅仅提供了拟合线性函数的能力,无法解决螺旋分类这样的非线性可分问题。
learning_rate = 1e-3
lambda_l2 = 1e-5
# 加入了ReLU激活函数
model = nn.Sequential(
nn.Linear(D, H),
nn.ReLU(),
nn.Linear(H, H),
nn.ReLU(),
nn.Linear(H, C)
)
model.to(device)
criterion = torch.nn.CrossEntropyLoss()
optimizer = torch.optim.Adam(model.parameters(), lr=learning_rate, weight_decay=lambda_l2)
for t in range(1000):
y_pred = model(X)
loss = criterion(y_pred, Y)
score, predicted = torch.max(y_pred, 1)
acc = ((Y == predicted).sum().float() / len(Y))
print("[EPOCH]: %i, [LOSS]: %.6f, [ACCURACY]: %.3f" % (t, loss.item(), acc))
display.clear_output(wait=True)
optimizer.zero_grad()
loss.backward()
optimizer.step()
在网络中加入了ReLU激活函数,以及每个epoch输出loss值和准确率
加入了ReLU激活函数后,网络可以拟合出非线性函数,从而具备了能够解决非线性可分问题的能力。在该示例展示的5分类螺旋分类任务中,经过1000个epoch正确率能够达到99.8%