引言
首先,在这里感谢Datawhale提供的免费学习的机会,感谢各位老师和助教的努力。在下小白一枚,也没有写过博客,借此机会,锻炼自己的写作。希望自己能够不断坚持,完成这两周的学习任务。短期目标:能够熟练的使用pytorch基础功能,构建完整的神经网络模型,发论文,走上人生巅峰!!!
第一次打卡的内容主要分为两部分:构建基础的pytorch模型(线性回归和分类模型)及基础RNN模型。
线性回归及分类模型
有监督模型中,回归问题和分类问题是两种常见的问题。回归和分类的区别在于输出变量的类型。回归问题的输出个数为1,并且其数值是连续的;分类问题的输出个数是类别数,代表的是各类别的得分或者概率,是离散的。事实上,在实际操作中,回归问题和分类问题也可以互相转化(分类问题回归化:逻辑回归;回归问题分类化:年龄预测问题>>>年龄段分类问题)。
线性回归
直接上栗子!!!房价预测问题:假设价格只取决于房屋状况的两个因素,即面积(平方米)和房龄(年)。因此,我们需要建立一个模型,研究房价和这两个因素(也可以叫做特征)存在的数学关系。线性回归的思想很直接,直接将他们考虑为线性关系。即模型为:
W
a
r
e
a
∗
a
r
e
a
+
W
a
g
e
∗
a
g
e
+
b
W_{area} * area + W_{age} * age + b
Warea∗area+Wage∗age+b
因为模型比较简单,在此示例中,使用的数据是我们自己构造的。损失函数使用的是均方误差:
L
o
s
s
=
1
n
∑
1
n
(
y
i
−
y
^
i
)
2
Loss = \frac{1}{n}\sum_{1}^{n}(y_{i} - \hat{y}_{i})^{2}
Loss=n11∑n(yi−y^i)2 优化函数使用的是随机梯度下降:
(
w
,
b
)
←
(
w
,
b
)
−
η
⋅
(
∂
∂
w
L
o
s
s
(
w
,
b
)
,
∂
∂
b
L
o
s
s
(
w
,
b
)
)
(w, b) \leftarrow (w, b) - \eta \cdot( \frac{\partial}{\partial w}Loss(w,b), \frac{\partial}{\partial b}Loss(w,b))
(w,b)←(w,b)−η⋅(∂w∂Loss(w,b),∂b∂Loss(w,b))其中,
η
\eta
η是学习率。具体代码如下:
import torch
from torch import nn
import numpy as np
import torch.utils.data as Data
from torch.nn import init
import torch.optim as optim
torch.manual_seed(1) # 设置随机种子,调用随机函数时,相同的随机种子会产生相同的随机数,便于试验复现
# 设置模型的一些超参数
num_inputs = 2 # 输入特征数
num_examples = 1000 # 样本个数
batch_size = 10 # 每次训练使用的数据批数
learning_rate = 0.03 # 定义学习率
num_epochs = 3 # 定义训练次数
###### 下面的代码是为了生成数据集 ######
# 假设的真实参数,模型训练参数越接近则表明模型越好
true_w = [2, -3.4]
true_b = 4.2
# 使用随机函数产生形状为(1000,2)的输入数据,代表1000个房屋的面积和房龄
features = torch.tensor(np.random.normal(0, 1, (num_examples, num_inputs)), dtype=torch.float)
# 计算房价
labels = true_w[0] * features[:, 0] + true_w[1] * features[:, 1] + true_b
# 使用随机函数,产生噪音,为了使数据更真实
labels += torch.tensor(np.random.normal(0, 0.01, size=labels.size()), dtype=torch.float)
###### 下面是模型定义 ######
# 将数据集放入dataset中,便于模型调用
dataset = Data.TensorDataset(features, labels)
# put dataset into DataLoader
data_iter = Data.DataLoader(
dataset=dataset, # torch TensorDataset format
batch_size=batch_size, # mini batch size
shuffle=True, # whether shuffle the data or not
num_workers=2, # read data in multithreading # window下会报错,注释掉即可
)
class LinearNet(nn.Module):
def __init__(self, n_feature):
super(LinearNet, self).__init__() # call father function to init
self.linear = nn.Linear(n_feature, 1) # function prototype: `torch.nn.Linear(in_features, out_features, bias=True)`
def forward(self, x):
y = self.linear(x)
return y
net = LinearNet(num_inputs) # 初始化模型
# print(net) # 可以打印模型,查看模型结构是否正确
###### 下面是参数初始化 ######
init.normal_(net[0].weight, mean=0.0, std=0.01)
init.constant_(net[0].bias, val=0.0)
# 定义损失函数
loss = nn.MSELoss()
# 定义优化函数
optimizer = optim.SGD(net.parameters(), lr=learning_rate)
###### 下面开始训练 ######
for epoch in range(1, num_epochs + 1):
for X, y in data_iter:
output = net(X) # 这里调用模型
l = loss(output, y.view(-1, 1)) # 计算损失
optimizer.zero_grad() # 梯度清零
l.backward() # 梯度反向传播
optimizer.step()
print('epoch %d, loss: %f' % (epoch, l.item()))
###### 训练结束,查看模型训练后的参数 ######
dense = net[0]
print(true_w, dense.weight.data)
print(true_b, dense.bias.data)
多层感知机
感知机(全连接神经网络)可以说是神经网络的开门算法,学习感知机的构造是通向神经网络和深度学习的一种重要思想。如下图(图片来源于网络)所示:
给定一个小批量样本
X
∈
R
n
×
d
X\in \mathbb{R}^{n\times d}
X∈Rn×d,
n
n
n为批量大小,
d
d
d为输入维度(图中为3),隐藏单元个数为
h
h
h(图中为3),输出维度为
q
q
q(图中为3)。假设只有一个隐藏层,隐藏层的输出记为
H
H
H,
H
∈
R
d
×
h
H\in \mathbb{R}^{d\times h}
H∈Rd×h,因此,输入层到隐藏层的权值参数和偏置参数为
W
h
∈
R
d
×
h
W_{h}\in \mathbb{R}^{d\times h}
Wh∈Rd×h和
b
h
∈
R
1
×
h
b_{h}\in \mathbb{R}^{1\times h}
bh∈R1×h,隐藏层到输出层的参数为
W
o
∈
R
h
×
q
W_{o}\in \mathbb{R}^{h\times q}
Wo∈Rh×q和
b
o
∈
R
1
×
q
b_{o}\in \mathbb{R}^{1\times q}
bo∈R1×q。其输出
O
∈
R
n
×
q
O \in \mathbb{R}^{n \times q}
O∈Rn×q的计算如下:
H
=
X
W
h
+
b
h
,
H = XW_{h} + b_{h},
H=XWh+bh,
H
′
=
δ
(
H
)
,
{H}' = \delta (H),
H′=δ(H),
O
=
H
′
W
o
+
b
o
,
O = {H}' W_{o} + b_{o},
O=H′Wo+bo, 其中
δ
\delta
δ表示的是激活函数。大多数情况下,中间层使用
R
e
L
U
ReLU
ReLU(只能用于中间层)作为激活函数,输出层使用
s
o
f
t
m
a
x
softmax
softmax(多分类)或
t
a
n
h
tanh
tanh(二分类)做为激活函数。
接下来上代码!!!图像分类走起!!!
import torch
from torch import nn
from torch.nn import init
import numpy as np
import sys
sys.path.append("/home/kesci/input")
import d2lzh1981 as d2l
# 设置一些超参数
num_inputs, num_outputs, num_hiddens = 784, 10, 256 # 输入、输出、隐藏单元个数
# 获取训练集
batch_size = 256
num_epochs = 5
learning_rate = 0.5
# 获得训练数据和测试数据
train_iter, test_iter = d2l.load_data_fashion_mnist(batch_size,root='/home/kesci/input/FashionMNIST2065')
# 定义模型
class TwoLayerNet(torch.nn.Module):
def __init__(self, num_inputs, num_hiddens , num_outputs):
super(TwoLayerNet, self).__init__()
self.linear1 = nn.Linear(num_inputs, num_hiddens )
self.linear2 = nn.Linear(num_hiddens , num_outputs)
def forward(self, x):
h_relu = self.linear1(x).clamp(min=0)
y_pred = self.linear2(h_relu)
return y_pred
# 模型初始化
model = TwoLayerNet(num_inputs, num_hiddens , num_outputs)
# 参数初始化
for params in model.parameters():
init.normal_(params, mean=0, std=0.01)
# 定义优化器
optimizer = torch.optim.SGD(net.parameters(), lr=learning_rate)
# 定义损失函数
loss = torch.nn.CrossEntropyLoss()
# 开始训练
for t in range(num_epochs):
train_l_sum, train_acc_sum, n = 0.0, 0.0, 0
for X, y in train_iter:
y_hat= model(x)
loss = loss(y_hat, y)
if optimizer is not None:
optimizer.zero_grad()
elif params is not None and params[0].grad is not None:
for param in params:
param.grad.data.zero_()
loss.backward()
if optimizer is None:
d2l.sgd(params, lr, batch_size)
else:
optimizer.step()
train_l_sum += loss.item()
train_acc_sum += (y_hat.argmax(dim=1) == y).sum().item()
n += y.shape[0]
test_acc = evaluate_accuracy(test_iter, net)
print('epoch %d, loss %.4f, train acc %.3f, test acc %.3f' % (epoch + 1, train_l_sum / n, train_acc_sum / n, test_acc))
文本预处理
自然语言处理(NLP)是深度学习的一大应用方向,文本是一类序列数据。然而,深度神经网络模型只接受数字作为输入,因此,在使用神经网络前,通常需要将文本进行预处理,转化为数字。本节介绍几个常用的预处理步骤。
读入文本
import collections
import re
def read_time_machine():
with open('timemachine.txt', 'r') as f:
lines = [re.sub('[^a-z]+', ' ', line.strip().lower()) for line in f] # 清理文本中的特殊字符
return lines
lines = read_time_machine()
print('# sentences %d' % len(lines)) # sentences 3221
建立字典
def count_corpus(sentences):
# sentences:[[I am FNLP], [I study pytorch in Datawhale]]
tokens = [tk for st in sentences for tk in st]
return collections.Counter(tokens) # 返回一个字典,记录每个词的出现次数
class Vocab(object):
# 输入:sentences:[[I am FNLP], [I study pytorch in Datawhale]]
def __init__(self, tokens, min_freq=0, use_special_tokens=True):
# 构建字典,统计词出现次数
counter = count_corpus(tokens)
self.token_freqs = list(counter.items())
self.idx_to_token = []
# 是否插入特殊标记,通常为True
if use_special_tokens:
self.pad, self.bos, self.eos, self.unk = (0, 1, 2, 3)
self.idx_to_token += ['', '', '', '']
else:
self.unk = 0
self.idx_to_token += ['']
self.idx_to_token += [token for token, freq in self.token_freqs
if freq >= min_freq and token not in self.idx_to_token]
self.token_to_idx = dict()
for idx, token in enumerate(self.idx_to_token):
self.token_to_idx[token] = idx
# 查看长度
def __len__(self):
return len(self.idx_to_token)
# 根据token(词)获取id
def __getitem__(self, tokens):
if not isinstance(tokens, (list, tuple)):
return self.token_to_idx.get(tokens, self.unk)
return [self.__getitem__(token) for token in tokens]
# 根据id 获取对应的词
def to_tokens(self, indices):
if not isinstance(indices, (list, tuple)):
return self.idx_to_token[indices]
return [self.idx_to_token[index] for index in indices]
总结
暂时先更新到这里,第一次学习的内容还包括语言模型和RNN基础,但是考虑到那两部分的应用并不多,只需要了解就行,而且这次更新的内容也比较多,就留待下次学习LSTM的时候在更新。再次感谢Datawhale提供的这次学习机会。