第一天作业打卡
第一天任务
线性回归
数据集的生成
num_inputs = 2
num_examples = 1000
true_w = [2, -3.4]
true_b = 4.2
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)
读取数据集
import torch.utils.data as Data
batch_size = 10
# combine featues and labels of 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
)
定义模型
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)
softmax
softmax运算符(softmax operator)解决了1.输出层的范围不确定;2.真实标签是离散的。它将输出值变换成值为正且和为1的概率分布:
第二天任务
文本预处理
文本预处理是针对一列序列数据进行处理,包含:
####1.文本读入
注意python正则表达式学习
2.分词
对每个句子进行分词,讲一个句子划分成若干个token,转换为一个词的序列
3.建立字典,将每个词映射到一个唯一的索引
构建字典,将每个词映射到唯一的索引编号
4.词的序列转换为索引序列,输入模型
使用字典,将将句子从单词序列转索引序列
语言模型
给定一段序列根据概率来判断序列的合理性,本章主要介绍基于 n n n元语法的语言模型
序列长度增加,计算和存储多个词共同出现的概率的复杂度会呈指数级增加。 n n n元语法通过马尔可夫假设简化模型,马尔科夫假设是指一个词的出现只与前面 n n n个词相关,即 n n n阶马尔可夫链。当 n n n较小时, n n n元语法往往并不准确。例如,在一元语法中,由三个词组成的句子“你走先”和“你先走”的 n n n较大时, n n n元语法需要计算并存储大量的词频和多词相邻频率。
n n n元语法存在参数空间过大,数据稀疏的特点。
1.时序数据采样
在训练中我们需要每次随机读取小批量样本和标签。与之前章节的实验数据不同的是,时序数据的一个样本通常包含连续的字符。 序列的长度为 T T T,时间步数为 n n n,那么一共有 T − n T−n T−n个合法的样本,但是这些样本有大量的重合,我们通常采用更加高效的采样方式。我们有两种方式对时序数据进行采样,分别是随机采样和相邻采样。
随机采样
下面的代码每次从数据里随机采样一个小批量。其中批量大小batch_size
是每个小批量的样本数,num_steps
是每个样本所包含的时间步数。 在随机采样中,每个样本是原始序列上任意截取的一段序列,相邻的两个随机小批量在原始序列上的位置不一定相毗邻。
import torch
import random
def data_iter_random(corpus_indices, batch_size, num_steps, device=None):
# 减1是因为对于长度为n的序列,X最多只有包含其中的前n - 1个字符
num_examples = (len(corpus_indices) - 1) // num_steps # 下取整,得到不重叠情况下的样本个数
example_indices = [i * num_steps for i in range(num_examples)] # 每个样本的第一个字符在corpus_indices中的下标
random.shuffle(example_indices)
def _data(i):
# 返回从i开始的长为num_steps的序列
return corpus_indices[i: i + num_steps]
if device is None:
device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
for i in range(0, num_examples, batch_size):
# 每次选出batch_size个随机样本
batch_indices = example_indices[i: i + batch_size] # 当前batch的各个样本的首字符的下标
X = [_data(j) for j in batch_indices]
Y = [_data(j + 1) for j in batch_indices]
yield torch.tensor(X, device=device), torch.tensor(Y, device=device)
相邻采样
在相邻采样中,相邻的两个随机小批量在原始序列上的位置相毗邻。
def data_iter_consecutive(corpus_indices, batch_size, num_steps, device=None):
if device is None:
device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
corpus_len = len(corpus_indices) // batch_size * batch_size # 保留下来的序列的长度
corpus_indices = corpus_indices[: corpus_len] # 仅保留前corpus_len个字符
indices = torch.tensor(corpus_indices, device=device)
indices = indices.view(batch_size, -1) # resize成(batch_size, )
batch_num = (indices.shape[1] - 1) // num_steps
for i in range(batch_num):
i = i * num_steps
X = indices[:, i: i + num_steps]
Y = indices[:, i + 1: i + num_steps + 1]
yield X, Y
循环神经网络
基于当前的输入与过去的输入序列,预测序列的下一个字符。循环神经网络引入一个隐藏变量 H H H,用 H t H_{t} Ht表示 H H H在时间步 t t t的值。 H t H_{t} Ht的计算基于 X t X_{t} Xt和 H t − 1 H_{t-1} Ht−1,可以认为 H t H_{t} Ht记录了到当前字符为止的序列信息,利用 H t H_{t} Ht对序列的下一个字符进行预测。
RNN
我们使用Pytorch中的nn.RNN
来构造循环神经网络。在本节中,我们主要关注nn.RNN
的以下几个构造函数参数:
input_size
- The number of expected features in the input xhidden_size
– The number of features in the hidden state hnonlinearity
– The non-linearity to use. Can be either ‘tanh’ or ‘relu’. Default: ‘tanh’batch_first
– If True, then the input and output tensors are provided as (batch_size, num_steps, input_size). Default: False
这里的batch_first
决定了输入的形状,我们使用默认的参数False
,对应的输入形状是 (num_steps, batch_size, input_size)。
forward
函数的参数为:
input
of shape (num_steps, batch_size, input_size): tensor containing the features of the input sequence.h_0
of shape (num_layers * num_directions, batch_size, hidden_size): tensor containing the initial hidden state for each element in the batch. Defaults to zero if not provided. If the RNN is bidirectional, num_directions should be 2, else it should be 1.
forward
函数的返回值是:
output
of shape (num_steps, batch_size, num_directions * hidden_size): tensor containing the output features (h_t) from the last layer of the RNN, for each t.h_n
of shape (num_layers * num_directions, batch_size, hidden_size): tensor containing the hidden state for t = num_steps.
class RNNModel(nn.Module):
def __init__(self, rnn_layer, vocab_size):
super(RNNModel, self).__init__()
self.rnn = rnn_layer
self.hidden_size = rnn_layer.hidden_size * (2 if rnn_layer.bidirectional else 1)
self.vocab_size = vocab_size
self.dense = nn.Linear(self.hidden_size, vocab_size)
def forward(self, inputs, state):
# inputs.shape: (batch_size, num_steps)
X = to_onehot(inputs, vocab_size)
X = torch.stack(X) # X.shape: (num_steps, batch_size, vocab_size)
hiddens, state = self.rnn(X, state)
hiddens = hiddens.view(-1, hiddens.shape[-1]) # hiddens.shape: (num_steps * batch_size, hidden_size)
output = self.dense(hiddens)
return output, state
GRU
RNN存在的问题:梯度较容易出现衰减或爆炸(BPTT) ⻔控循环神经⽹络:捕捉时间序列中时间步距离较⼤的依赖关系
def gru(inputs, state, params):
W_xz, W_hz, b_z, W_xr, W_hr, b_r, W_xh, W_hh, b_h, W_hq, b_q = params
H, = state
outputs = []
for X in inputs:
Z = torch.sigmoid(torch.matmul(X, W_xz) + torch.matmul(H, W_hz) + b_z)
R = torch.sigmoid(torch.matmul(X, W_xr) + torch.matmul(H, W_hr) + b_r)
H_tilda = torch.tanh(torch.matmul(X, W_xh) + R * torch.matmul(H, W_hh) + b_h)
H = Z * H + (1 - Z) * H_tilda
Y = torch.matmul(H, W_hq) + b_q
outputs.append(Y)
return outputs, (H,)
####LSTM
长短期记忆long short-term memory : 遗忘门:控制上一时间步的记忆细胞 输入门:控制当前时间步的输入 输出门:控制从记忆细胞到隐藏状态 记忆细胞:⼀种特殊的隐藏状态的信息的流动
def lstm(inputs, state, params):
[W_xi, W_hi, b_i, W_xf, W_hf, b_f, W_xo, W_ho, b_o, W_xc, W_hc, b_c, W_hq, b_q] = params
(H, C) = state
outputs = []
for X in inputs:
I = torch.sigmoid(torch.matmul(X, W_xi) + torch.matmul(H, W_hi) + b_i)
F = torch.sigmoid(torch.matmul(X, W_xf) + torch.matmul(H, W_hf) + b_f)
O = torch.sigmoid(torch.matmul(X, W_xo) + torch.matmul(H, W_ho) + b_o)
C_tilda = torch.tanh(torch.matmul(X, W_xc) + torch.matmul(H, W_hc) + b_c)
C = F * C + I * C_tilda
H = O * C.tanh()
Y = torch.matmul(H, W_hq) + b_q
outputs.append(Y)
return outputs, (H, C)