第一次作业:代码练习
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中。

第一次作业:代码练习_homework

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()

学习率:也即步长,是误差反向传播中的一个参数。用于控制每一次更新参数利用多少误差。

第一次作业:代码练习_Mechain Learning_02

学习率越大,输出误差对参数的影响就越大,参数更新的就越快,但同时受到异常数据的影响就越大,容易在极值附近震荡。

学习率过小又会导致收敛速度较慢,且容易过拟合。

第一次作业:代码练习_数据_03

得到的预测结果

两层全连接层仅仅提供了拟合线性函数的能力,无法解决螺旋分类这样的非线性可分问题。

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值和准确率

第一次作业:代码练习_反向传播_04

第一次作业:代码练习_激活函数_05

加入了ReLU激活函数后,网络可以拟合出非线性函数,从而具备了能够解决非线性可分问题的能力。在该示例展示的5分类螺旋分类任务中,经过1000个epoch正确率能够达到99.8%