1 3.3 线性回归的简洁实现
2 import torch
3 from torch import nn
4 import numpy as np
5 torch.manual_seed(1)
6
7 print(torch.__version__)
8 torch.set_default_tensor_type('torch.FloatTensor')
9
10 # 1 生成数据集
11 num_inputs = 2 # 输入个数(特征数)
12 num_examples = 1000 # 训练数据集样本数
13 true_w = [2, -3.4] # 真实权重
14 true_b = 4.2 # 偏差
15
16 # features:训练数据特征;labels:标签
17 features = torch.tensor(np.random.normal(0, 1, (num_examples, num_inputs)), dtype= torch.float)
18 labels = true_w[0] * features[:, 0] + true_w[1] * features[:, 1] + true_b # y = Xw + b + e
19 labels += torch.tensor(np.random.normal(0, 0.01, size=labels.size()), dtype=torch.float)
20
21 # Passage 2 读取数据
22 import torch.utils.data as Data
23
24 batch_size = 10
25
26 # 将训练数据的特征和标签组合
27 dataset = Data.TensorDataset(features, labels)
28
29 # 把dataset放入DataLoader,随机读取小批量
30 data_iter = Data.DataLoader(
31 dataset=dataset, # torch TensorDataset format
32 batch_size=batch_size, # mini batch size
33 shuffle=True, # 要不要打乱数据 (打乱比较好)
34 # num_workers=Passage 2, # 多线程来读数据
35 )
36
37 # 读取并打印第一个小批量数据样本
38 for X, y in data_iter:
39 print(X, '\n', y)
40 break
41
42 # Passage 3 定义模型
43 # 导入torch.nn模块(neural networks,神经网络)
44 # nn的核心数据结构为Module,抽象概念,既可以表示神经网络中的某个层,也可以表示一个包含很多层的神经网络
45 # 常见做法:继承nn.Module,撰写自己的网络/层
46 # 一个nn.Module实例应该包含一些层以及返回输出的向前传播方法
47 class LinearNet(nn.Module):
48 def __init__(self, n_feature):
49 super(LinearNet, self).__init__()
50 self.linear = nn.Linear(n_feature, 1)
51 # forward 定义向前传播
52 def forward(self, x):
53 y = self.linear(x)
54 return y
55
56 net = LinearNet(num_inputs)
57 print(net) # 打印出网络的结构
58
59 # nn.Sequential
60 # Sequential是一个有序的容器,网络层将按照传入Sequential的顺序依次被添加到计算图中
61 # 写法一:
62 net = nn.Sequential(
63 nn.Linear(num_inputs, 1)
64 # 此处还可以传入其他层
65 )
66
67 # 写法二:
68 net = nn.Sequential()
69 net.add_module('linear', nn.Linear(num_inputs, 1))
70 # net.add_module......
71
72 # 写法三:
73 from collections import OrderedDict
74 net = nn.Sequential(OrderedDict([
75 ('linear', nn.Linear(num_inputs, 1))
76 #......
77 ]))
78
79 print(net)
80 print(net[0])
81
82 # 通过net.parameters()来查看模型所有的可学习参数,将返回一个生成器
83 for param in net.parameters():
84 print(param)
85
86 # 作为一个单层神经网络,线性回归输出层中的神经元和输入层中各个输入完全连接;因此,线性回归的输出层又叫全连接层
87 # 注意:torch.nn仅支持输入一个batch的样本不支持单个样本输入,如果只有单个样本,可使用input.unsqueeze(0)来添加一维
88
89 # 4 初始化模型参数,例如线性回归中的权重和偏差
90 # 通过init.normal_将权重参数每个元素初始化为随机采样于均值为0,标注差为0.01的正态分布,偏差会初始化为零
91 from torch.nn import init
92
93 init.normal_(net[0].weight, mean=0, std=0.01)
94 init.constant_(net[0].bias, val=0.0)
95 # 可以直接修改bias的data: net[0].bias.data.fill_(0)
96
97 for param in net.parameters():
98 print(param)
99
100 # 5 定义损失函数
101 # 损失函数可看作是一种特殊的层
102 loss = nn.MSELoss() # 使用均方误差
103
104 # 6 定义优化算法
105 # torch.optim模块提供了很多常用的优化算法,例如:SGD,Adam和RMSProp等
106 # 创建一个用于优化net所有参数的优化器实例,并指定学习率为0.03的小批量随机梯度下降(SGD)为优化算法
107 import torch.optim as optim
108
109 optimizer = optim.SGD(net.parameters(), lr=0.03)
110 print(optimizer)
111
112 # 还可以为不同子网络设置不同的学习率,在finetune时经常用到
113 # optimizer = optim.SGD([
114 # # 如果对某个参数不指定学习率,就使用最外层的默认学习率
115 # {'params': net.subnet1.parameters()}, # lr=0.01
116 # {'params': net.subnet2.parameters(), 'lr': 0.01}
117 # ], lr=0.03)
118
119 # 调整学习率
120 # 法一:修改optimizer.param.groups中对应的学习率
121 # 法二(推荐):新建优化器,由于optimizer十分轻量级,构建开销很小,故而可以构建新的optimizer
122 # 但是后者对于使用动量的优化器(如Adam),会丢失动等状态信息,可能会造成损失函数的收敛出现震荡等情况
123 # for param_group in optimizer.param_groups:
124 # param_group['lr'] *= 0.1 # 学习率为之前的0.1倍
125
126 # 7 训练模型
127 num_epochs = 3
128 for epoch in range(1, num_epochs + 1):
129 for X, y in data_iter:
130 output = net(X)
131 l = loss(output, y.view(-1, 1))
132 optimizer.zero_grad() # 梯度清零,等价于net.zero_grad()
133 l.backward()
134 optimizer.step()
135 print('epoch %d, loss: %f' % (epoch, l.item()))
136
137 dense = net[0]
138 print(true_w, dense.weight.data)
139 print(true_b, dense.bias.data)