当前位置: 首页 > news >正文

深度学习复盘与论文复现C

文章目录

  • 4、Distributed training
    • 4.1 GPU architecture
  • 5、Recurrent neural network
    • 5.1 The basic structure of RNN
    • 5.2 Neural networks without hidden states
    • 5.3 Recurrent neural networks with hidden states
    • 5.4 summary
  • 6、Language Model Dataset (lyrics from Jay Chou's album)
    • 6.1 Read Dataset
    • 6.2 Establish character index
    • 6.3 Sampling of temporal data
      • 6.3.1 Randomize Samples
      • 6.3.2 Adjacent sampling
  • 7、Implementation of Recurrent Neural Networks from scratch
    • 7.1 one-hot vector
    • 7.2 Initialize model parameters
    • 7.3 definition model
    • 7.4 Define prediction function
    • 7.5 Crop gradient
    • 7.6 perplexity
    • 7.7 Define model training function
  • 8、A concise implementation of recurrent neural networks
    • 8.1 definition model
    • 8.2 training model
    • 8.3 brief summary
  • 9、Kaggle house_price achieve
    • 9.1 Implement the address
    • 9.2 Implement screenshots

4、Distributed training

4.1 GPU architecture

在这里插入图片描述

5、Recurrent neural network

RNN(Recurrent Neural Network,循环神经网络)是一种具有“记忆”功能的神经网络,它特别适合处理序列数据,如文本、语音、时间序列等。RNN通过在其内部结构中引入循环,使得网络能够捕捉序列中的时间依赖性和动态性。

5.1 The basic structure of RNN

  • RNN 的核心部分是一个循环单元,它接收当前的输入 x_t 和前一个时间步的隐藏状态 h_{t-1},然后输出一个当前时间步的隐藏状态 h_t 和一个可选的输出 y_t。隐藏状态 h_t 包含了到目前为止观察到的序列的信息,它可以被看作是网络的“记忆”。

RNN 的公式表示

在 RNN 中,隐藏状态 h_t 和输出 y_t 的计算通常使用以下公式:

  • 隐藏状态的计算:h_t = f(W_xh * x_t + W_hh * h_{t-1} + b_h)
  • 输出的计算(如果需要):y_t = g(W_hy * h_t + b_y)

其中:

  • fg 是激活函数,如 sigmoid、tanh 或 ReLU。
  • W_xhW_hhW_hy 是权重矩阵。
  • b_hb_y 是偏置项。

RNN 的问题

尽管 RNN 在处理序列数据方面非常有效,但它也面临一些问题:

  1. 梯度消失/梯度爆炸:当处理长序列时,RNN 可能会遇到梯度消失或梯度爆炸的问题,这会导致网络难以学习长期依赖关系。
  2. 计算效率:RNN 在处理每个时间步时都需要重新计算隐藏状态,这导致计算效率较低。

RNN 的变体

为了解决这些问题,研究人员提出了 RNN 的多种变体,其中最著名的是 LSTM(Long Short-Term Memory,长短期记忆)和 GRU(Gated Recurrent Unit,门控循环单元)。这些变体通过引入门控机制和内部状态来改进 RNN 的性能。

  • LSTM:LSTM 通过引入输入门、遗忘门和输出门来控制信息的流动,并使用内部状态来存储长期信息。这使得 LSTM 能够更好地学习长期依赖关系。

  • GRU:GRU 是 LSTM 的一个简化版本,它只包含重置门和更新门,但仍然能够捕获长期依赖关系。GRU 的计算效率通常比 LSTM 更高。

    应用场景

RNN 及其变体在许多领域都有广泛的应用,包括:

  • 自然语言处理:如文本生成、机器翻译、情感分析等。
  • 语音识别:将语音转换为文本。
  • 时间序列预测:如股票价格预测、天气预测等。
  • 音乐生成:基于旋律和节奏生成新的音乐。
  • 推荐系统:根据用户的历史行为预测其未来的兴趣。

5.2 Neural networks without hidden states

  • 考虑一个含单隐藏层的多层感知机。给定样本数为 n n n、输入个数(特征数或特征向量维度)为 d d d的小批量数据样本 X ∈ R n × d \boldsymbol{X} \in \mathbb{R}^{n \times d} XRn×d。设隐藏层的激活函数为 ϕ \phi ϕ,那么隐藏层的输出 H ∈ R n × h \boldsymbol{H} \in \mathbb{R}^{n \times h} HRn×h计算为

H = ϕ ( X W x h + b h ) , \boldsymbol{H} = \phi(\boldsymbol{X} \boldsymbol{W}_{xh} + \boldsymbol{b}_h), H=ϕ(XWxh+bh),

其中隐藏层权重参数 W x h ∈ R d × h \boldsymbol{W}_{xh} \in \mathbb{R}^{d \times h} WxhRd×h,隐藏层偏差参数 b h ∈ R 1 × h \boldsymbol{b}_h \in \mathbb{R}^{1 \times h} bhR1×h h h h为隐藏单元个数。上式相加的两项形状不同,因此将按照广播机制相加。把隐藏变量 H \boldsymbol{H} H作为输出层的输入,且设输出个数为 q q q(如分类问题中的类别数),输出层的输出为

O = H W h q + b q , \boldsymbol{O} = \boldsymbol{H} \boldsymbol{W}_{hq} + \boldsymbol{b}_q, O=HWhq+bq,

其中输出变量 O ∈ R n × q \boldsymbol{O} \in \mathbb{R}^{n \times q} ORn×q, 输出层权重参数 W h q ∈ R h × q \boldsymbol{W}_{hq} \in \mathbb{R}^{h \times q} WhqRh×q, 输出层偏差参数 b q ∈ R 1 × q \boldsymbol{b}_q \in \mathbb{R}^{1 \times q} bqR1×q。如果是分类问题,我们可以使用 softmax ( O ) \text{softmax}(\boldsymbol{O}) softmax(O)来计算输出类别的概率分布。

5.3 Recurrent neural networks with hidden states

import torchX, W_xh = torch.randn(3, 1), torch.randn(1, 4)
H, W_hh = torch.randn(3, 4), torch.randn(4, 4)
torch.matmul(X, W_xh) + torch.matmul(H, W_hh)

输出:

tensor([[ 5.2633, -3.2288,  0.6037, -1.3321],[ 9.4012, -6.7830,  1.0630, -0.1809],[ 7.0355, -2.2361,  0.7469, -3.4667]])
  • 将矩阵XH按列(维度1)连结,连结后的矩阵形状为(3, 5)。可见,连结后矩阵在维度1的长度为矩阵XH在维度1的长度之和( 1 + 4 1+4 1+4)。然后,将矩阵W_xhW_hh按行(维度0)连结,连结后的矩阵形状为(5, 4)。最后将两个连结后的矩阵相乘,得到与上面代码输出相同的形状为(3, 4)的矩阵。
torch.matmul(torch.cat((X, H), dim=1), torch.cat((W_xh, W_hh), dim=0))

输出:

tensor([[ 5.2633, -3.2288,  0.6037, -1.3321],[ 9.4012, -6.7830,  1.0630, -0.1809],[ 7.0355, -2.2361,  0.7469, -3.4667]])

5.4 summary

  • 使用循环计算的网络即循环神经网络。
  • 循环神经网络的隐藏状态可以捕捉截至当前时间步的序列的历史信息。
  • 循环神经网络模型参数的数量不随时间步的增加而增长。
  • 可以基于字符级循环神经网络来创建语言模型。

6、Language Model Dataset (lyrics from Jay Chou’s album)

  • 预处理一个语言模型数据集,并将其转换成字符级循环神经网络所需要的输入格式。

6.1 Read Dataset

import torch
import random
import zipfilewith zipfile.ZipFile('../../data/jaychou_lyrics.txt.zip') as zin:with zin.open('jaychou_lyrics.txt') as f:corpus_chars = f.read().decode('utf-8')
corpus_chars[:40]

输出:

'想要有直升机\n想要和你飞到宇宙去\n想要和你融化在一起\n融化在宇宙里\n我每天每天每'
  • 为了打印方便,我们把换行符替换成空格,然后仅使用前1万个字符来训练模型。
corpus_chars = corpus_chars.replace('\n', ' ').replace('\r', ' ')
corpus_chars = corpus_chars[0:10000]

6.2 Establish character index

  • 我们将每个字符映射成一个从0开始的连续整数,又称索引,来方便之后的数据处理。为了得到索引,我们将数据集里所有不同字符取出来,然后将其逐一映射到索引来构造词典。接着,打印vocab_size,即词典中不同字符的个数,又称词典大小。
idx_to_char = list(set(corpus_chars))
char_to_idx = dict([(char, i) for i, char in enumerate(idx_to_char)])
vocab_size = len(char_to_idx)
vocab_size # 1027

之后,将训练数据集中每个字符转化为索引,并打印前20个字符及其对应的索引。

corpus_indices = [char_to_idx[char] for char in corpus_chars]
sample = corpus_indices[:20]
print('chars:', ''.join([idx_to_char[idx] for idx in sample]))
print('indices:', sample)

输出:

chars: 想要有直升机 想要和你飞到宇宙去 想要和
indices: [250, 164, 576, 421, 674, 653, 357, 250, 164, 850, 217, 910, 1012, 261, 275, 366, 357, 250, 164, 850]

6.3 Sampling of temporal data

  • 在训练中我们需要每次随机读取小批量样本和标签。与之前章节的实验数据不同的是,时序数据的一个样本通常包含连续的字符。假设时间步数为5,样本序列为5个字符,即“想”“要”“有”“直”“升”。该样本的标签序列为这些字符分别在训练集中的下一个字符,即“要”“有”“直”“升”“机”。我们有两种方式对时序数据进行采样,分别是随机采样和相邻采样。

6.3.1 Randomize Samples

  1. 参数:

    • corpus_indices: 文本序列的索引表示。通常,每个字符都被映射到一个唯一的索引。
    • batch_size: 每个批次中样本的数量。
    • num_steps: 每个样本中字符的数量(即序列长度)。
    • device: 用于存储张量的设备(如 CPU 或 GPU)。默认为 None,函数内部会根据是否有可用的 GPU 来自动选择。
  2. 计算样本数量:

    • num_examples = (len(corpus_indices) - 1) // num_steps: 由于每个样本都需要num_steps个字符,且最后一个字符后面没有下一个字符,所以可用的样本数量是总字符数减去1后除以num_steps的整数部分。
  3. 计算轮次大小:

    • epoch_size = num_examples // batch_size: 一个轮次(epoch)中可以包含的批次数量。
  4. 打乱样本索引:

    • 使用random.shuffle(example_indices)来打乱样本索引,这样每次迭代时都可以从随机位置抽取样本。
  5. 定义内部函数 _data:

    • 这个函数用于从打乱后的索引中抽取长度为num_steps的序列。
  6. 选择设备:

    • 如果device为None,则根据是否有可用的 GPU 自动选择设备。
  7. 迭代生成批次:

    • 对于每个批次,从打乱后的索引中抽取batch_size个样本的索引,并使用_data函数从corpus_indices中提取相应的序列。
    • X 是输入序列,其中每个序列都是长度为num_steps的字符索引列表。
    • Y 是目标序列,其中每个序列是X中对应序列的下一个字符的索引列表。
    • 使用torch.tensor将这些索引列表转换为张量,并指定数据类型为torch.float32和所选的设备。
    • 使用yield关键字将张量对(X, Y)作为生成器的一部分返回。
def data_iter_random(corpus_indices, batch_size, num_steps, device=None):# 减1是因为输出的索引x是相应输入的索引y加1num_examples = (len(corpus_indices) - 1) // num_stepsepoch_size = num_examples // batch_sizeexample_indices = list(range(num_examples))random.shuffle(example_indices)# 返回从pos开始的长为num_steps的序列def _data(pos):return corpus_indices[pos: pos + num_steps]if device is None:device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')for i in range(epoch_size):# 每次读取batch_size个随机样本i = i * batch_sizebatch_indices = example_indices[i: i + batch_size]X = [_data(j * num_steps) for j in batch_indices]Y = [_data(j * num_steps + 1) for j in batch_indices]yield torch.tensor(X, dtype=torch.float32, device=device), torch.tensor(Y, dtype=torch.float32, device=device)
my_seq = list(range(30))
for X, Y in data_iter_random(my_seq, batch_size=2, num_steps=6):print('X: ', X, '\nY:', Y, '\n')

输出:

在这里插入图片描述

6.3.2 Adjacent sampling

  • corpus_indices: 文本序列的索引表示。
  • batch_size: 每个批次中样本的数量。
  • num_steps: 每个样本中字符的数量(即序列长度)。
  • device: 用于存储张量的设备(如 CPU 或 GPU)。默认为 None,函数内部会根据是否有可用的 GPU 来自动选择。
  1. 选择设备:
    如果 device 为 None,则根据是否有可用的 GPU 自动选择设备。

  2. 转换数据类型和设备:
    corpus_indices 转换为 PyTorch 张量,并指定数据类型为 torch.float32 和所选的设备。

  3. 确定批次长度:
    由于我们想要从整个 corpus_indices 中创建连续的批次,我们首先确保 corpus_indices 的长度是 batch_size 的整数倍。如果不是,我们只取前 batch_size * batch_len 个索引,其中 batch_lencorpus_indices 的长度除以 batch_size 的整数部分。

  4. 重新塑造数据:
    将索引张量重新塑造为二维的 (batch_size, batch_len) 形状,其中 batch_len 是每个批次中的时间步数(不是 num_steps)。

  5. 确定轮次大小:
    因为每个批次中的序列是连续的,并且我们希望每个样本的长度为 num_steps,所以我们从每个批次中可以得到的最大样本数是 (batch_len - 1) // num_steps

  6. 迭代生成批次:
    对于每个轮次(epoch)中的每个步骤,我们从重新塑造的二维张量 indices 中选择连续的 num_steps 长的序列作为输入 X,并将这些序列向右移动一个位置来得到目标 Y(即每个输入序列的下一个字符)。

  7. 返回数据:
    使用 yield 关键字将张量对 (X, Y) 作为生成器的一部分返回。

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_indices = torch.tensor(corpus_indices, dtype=torch.float32, device=device)data_len = len(corpus_indices)batch_len = data_len // batch_sizeindices = corpus_indices[0: batch_size*batch_len].view(batch_size, batch_len)epoch_size = (batch_len - 1) // num_stepsfor i in range(epoch_size):i = i * num_stepsX = indices[:, i: i + num_steps]Y = indices[:, i + 1: i + num_steps + 1]yield X, Y

同样的设置下,打印相邻采样每次读取的小批量样本的输入X和标签Y。相邻的两个随机小批量在原始序列上的位置相毗邻。

for X, Y in data_iter_consecutive(my_seq, batch_size=2, num_steps=6):print('X: ', X, '\nY:', Y, '\n')

输出:

X:  tensor([[ 0.,  1.,  2.,  3.,  4.,  5.],[15., 16., 17., 18., 19., 20.]]) 
Y: tensor([[ 1.,  2.,  3.,  4.,  5.,  6.],[16., 17., 18., 19., 20., 21.]]) X:  tensor([[ 6.,  7.,  8.,  9., 10., 11.],[21., 22., 23., 24., 25., 26.]]) 
Y: tensor([[ 7.,  8.,  9., 10., 11., 12.],[22., 23., 24., 25., 26., 27.]]) 

在这里插入图片描述

  • 时序数据采样方式包括随机采样和相邻采样。使用这两种方式的循环神经网络训练在实现上略有不同。

7、Implementation of Recurrent Neural Networks from scratch

import time
import math
import numpy as np
import torch
from torch import nn, optim 
import torch.nn.functional as Fimport sys
sys.path.append("..") 
import d2lzh_pytorch as d2l
device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')(corpus_indices, char_to_idx, idx_to_char, vocab_size) = d2l.load_data_jay_lyrics()

7.1 one-hot vector

  • 为了将词表示成向量输入到神经网络,一个简单的办法是使用one-hot向量。假设词典中不同字符的数量为 N N N(即词典大小vocab_size),每个字符已经同一个从0到 N − 1 N-1 N1的连续整数值索引一一对应。如果一个字符的索引是整数 i i i, 那么我们创建一个全0的长为 N N N的向量,并将其位置为 i i i的元素设成1。该向量就是对原字符的one-hot向量。下面分别展示了索引为0和2的one-hot向量,向量长度等于词典大小。
def one_hot(x, n_class, dtype=torch.float32): # X shape: (batch), output shape: (batch, n_class)x = x.long()res = torch.zeros(x.shape[0], n_class, dtype=dtype, device=x.device)res.scatter_(1, x.view(-1, 1), 1)return resx = torch.tensor([0, 2])
one_hot(x, vocab_size)
  • 每次采样的小批量的形状是(批量大小, 时间步数)。下面的函数将这样的小批量变换成数个可以输入进网络的形状为(批量大小, 词典大小)的矩阵,矩阵个数等于时间步数。也就是说,时间步 t t t的输入为 X t ∈ R n × d \boldsymbol{X}_t \in \mathbb{R}^{n \times d} XtRn×d,其中 n n n为批量大小, d d d为输入个数,即one-hot向量长度(词典大小)。
def to_onehot(X, n_class):  # X shape: (batch, seq_len), output: seq_len elements of (batch, n_class)return [one_hot(X[:, i], n_class) for i in range(X.shape[1])]X = torch.arange(10).view(2, 5)
inputs = to_onehot(X, vocab_size)
print(len(inputs), inputs[0].shape)

输出:

5 torch.Size([2, 1027])

7.2 Initialize model parameters

  1. 参数定义

    • num_inputs:输入层的大小,等于词汇表大小(vocab_size)。
    • num_hiddens:隐藏层的大小,这里设置为 256。
    • num_outputs:输出层的大小,也等于词汇表大小(vocab_size),因为对于每个时间步,我们都要预测一个词(通常是使用 one-hot 编码或者词嵌入)。
    • device:用于指定参数应该存储在哪个设备上(CPU 或 GPU)。
  2. 打印设备

    • 通过 print('will use', device) 打印将要使用的设备。
  3. get_params 函数

    • 该函数返回一个包含 RNN 所有参数的 nn.ParameterList
    • 隐藏层参数:
      • W_xh:从输入层到隐藏层的权重矩阵,大小为 (num_inputs, num_hiddens)
      • W_hh:从上一时间步的隐藏状态到当前时间步的隐藏状态的权重矩阵,大小为 (num_hiddens, num_hiddens)
      • b_h:隐藏层的偏置向量,大小为 (num_hiddens,)
    • 输出层参数:
      • W_hq:从隐藏层到输出层的权重矩阵,大小为 (num_hiddens, num_outputs)
      • b_q:输出层的偏置向量,大小为 (num_outputs,)
    • 这些参数都使用正态分布(均值为 0,标准差为 0.01)进行初始化,并使用 torch.nn.Parameter 包裹以确保它们可以作为模型的参数被优化。
num_inputs, num_hiddens, num_outputs = vocab_size, 256, vocab_size
print('will use', device)def get_params():def _one(shape):ts = torch.tensor(np.random.normal(0, 0.01, size=shape), device=device, dtype=torch.float32)return torch.nn.Parameter(ts, requires_grad=True)# 隐藏层参数W_xh = _one((num_inputs, num_hiddens))W_hh = _one((num_hiddens, num_hiddens))b_h = torch.nn.Parameter(torch.zeros(num_hiddens, device=device, requires_grad=True))# 输出层参数W_hq = _one((num_hiddens, num_outputs))b_q = torch.nn.Parameter(torch.zeros(num_outputs, device=device, requires_grad=True))return nn.ParameterList([W_xh, W_hh, b_h, W_hq, b_q])

7.3 definition model

  • 根据循环神经网络的计算表达式实现该模型。首先定义init_rnn_state函数来返回初始化的隐藏状态。它返回由一个形状为(批量大小, 隐藏单元个数)的值为0的NDArray组成的元组。使用元组是为了更便于处理隐藏状态含有多个NDArray的情况。
def init_rnn_state(batch_size, num_hiddens, device):return (torch.zeros((batch_size, num_hiddens), device=device), )

下面的rnn函数定义了在一个时间步里如何计算隐藏状态和输出。这里的激活函数使用了tanh函数。

def rnn(inputs, state, params):# inputs和outputs皆为num_steps个形状为(batch_size, vocab_size)的矩阵W_xh, W_hh, b_h, W_hq, b_q = paramsH, = stateoutputs = []for X in inputs:H = torch.tanh(torch.matmul(X, W_xh) + torch.matmul(H, W_hh) + b_h)Y = torch.matmul(H, W_hq) + b_qoutputs.append(Y)return outputs, (H,)
state = init_rnn_state(X.shape[0], num_hiddens, device)
inputs = to_onehot(X.to(device), vocab_size)
params = get_params()
outputs, state_new = rnn(inputs, state, params)
print(len(outputs), outputs[0].shape, state_new[0].shape) 

输出:

5 torch.Size([2, 1027]) torch.Size([2, 256])

7.4 Define prediction function

  1. 函数参数:

    • prefix: 一个字符串,作为预测的起始字符或字符序列。
    • num_chars: 要预测的字符数量(不包括 prefix 中的字符)。
    • rnn: RNN 模型,它应该是一个函数,接受输入、隐藏状态和参数,并返回输出和新的隐藏状态。
    • params: RNN 模型的参数。
    • init_rnn_state: 一个函数,用于初始化 RNN 的隐藏状态。
    • num_hiddens: 隐藏层的大小。
    • vocab_size: 词汇表的大小。
    • device: 设备(CPU 或 GPU),用于指定在哪里执行计算。
    • idx_to_char: 一个字典,将索引映射到字符。
    • char_to_idx: 一个字典,将字符映射到索引。
  2. 初始化:

    • 使用 init_rnn_state 函数初始化 RNN 的隐藏状态。
    • 初始化一个列表 output,用于存储预测的字符索引。首先,将 prefix 的第一个字符的索引添加到 output 中。
  3. 预测循环:

    • 对于 num_chars + len(prefix) - 1 次迭代(包括 prefix 中的字符和要预测的字符),执行以下操作:
      • 将上一个时间步的输出(字符索引)转换为 one-hot 编码,并作为当前时间步的输入。
      • 使用 RNN 模型、输入和当前隐藏状态计算输出和新的隐藏状态。
      • 如果当前时间步还在 prefix 之内,那么将 prefix 中的下一个字符的索引添加到 output 中。否则,从 RNN 的输出中选择概率最高的字符索引,并添加到 output 中。
  4. 返回结果:

    • 使用 idx_to_char 字典将 output 中的索引转换为字符,并将这些字符连接成一个字符串返回。
def predict_rnn(prefix, num_chars, rnn, params, init_rnn_state,num_hiddens, vocab_size, device, idx_to_char, char_to_idx):state = init_rnn_state(1, num_hiddens, device)output = [char_to_idx[prefix[0]]]for t in range(num_chars + len(prefix) - 1):# 将上一时间步的输出作为当前时间步的输入X = to_onehot(torch.tensor([[output[-1]]], device=device), vocab_size)# 计算输出和更新隐藏状态(Y, state) = rnn(X, state, params)# 下一个时间步的输入是prefix里的字符或者当前的最佳预测字符if t < len(prefix) - 1:output.append(char_to_idx[prefix[t + 1]])else:output.append(int(Y[0].argmax(dim=1).item()))return ''.join([idx_to_char[i] for i in output])
  • 测试一下predict_rnn函数。我们将根据前缀“分开”创作长度为10个字符(不考虑前缀长度)的一段歌词。因为模型参数为随机值,所以预测结果也是随机的。
predict_rnn('分开', 10, rnn, params, init_rnn_state, num_hiddens, vocab_size,device, idx_to_char, char_to_idx)

输出:

'分开西圈绪升王凝瓜必客映'

7.5 Crop gradient

  • 循环神经网络中较容易出现梯度衰减或梯度爆炸。为了应对梯度爆炸,我们可以裁剪梯度(clip gradient)。假设我们把所有模型参数梯度的元素拼接成一个向量 g \boldsymbol{g} g,并设裁剪的阈值是 θ \theta θ。裁剪后的梯度

min ⁡ ( θ ∥ g ∥ , 1 ) g \min\left(\frac{\theta}{\|\boldsymbol{g}\|}, 1\right)\boldsymbol{g} min(gθ,1)g

L 2 L_2 L2范数不超过 θ \theta θ

def grad_clipping(params, theta, device):norm = torch.tensor([0.0], device=device)for param in params:norm += (param.grad.data ** 2).sum()norm = norm.sqrt().item()if norm > theta:for param in params:param.grad.data *= (theta / norm)

7.6 perplexity

  • 通常使用困惑度(perplexity)来评价语言模型的好坏。softmax回归中交叉熵损失函数的定义。困惑度是对交叉熵损失函数做指数运算后得到的值。特别地,
  • 最佳情况下,模型总是把标签类别的概率预测为1,此时困惑度为1;
  • 最坏情况下,模型总是把标签类别的概率预测为0,此时困惑度为正无穷;
  • 基线情况下,模型总是预测所有类别的概率都相同,此时困惑度为类别个数。

显然,任何一个有效模型的困惑度必须小于类别个数。在本例中,困惑度必须小于词典大小vocab_size

7.7 Define model training function

  1. 使用困惑度评价模型。
  2. 在迭代模型参数前裁剪梯度。
  3. 对时序数据采用不同采样方法将导致隐藏状态初始化的不同。
def train_and_predict_rnn(rnn, get_params, init_rnn_state, num_hiddens,vocab_size, device, corpus_indices, idx_to_char,char_to_idx, is_random_iter, num_epochs, num_steps,lr, clipping_theta, batch_size, pred_period,pred_len, prefixes):if is_random_iter:data_iter_fn = d2l.data_iter_randomelse:data_iter_fn = d2l.data_iter_consecutiveparams = get_params()loss = nn.CrossEntropyLoss()for epoch in range(num_epochs):if not is_random_iter:  # 如使用相邻采样,在epoch开始时初始化隐藏状态state = init_rnn_state(batch_size, num_hiddens, device)l_sum, n, start = 0.0, 0, time.time()data_iter = data_iter_fn(corpus_indices, batch_size, num_steps, device)for X, Y in data_iter:if is_random_iter:  # 如使用随机采样,在每个小批量更新前初始化隐藏状态state = init_rnn_state(batch_size, num_hiddens, device)else:  # 否则需要使用detach函数从计算图分离隐藏状态, 这是为了# 使模型参数的梯度计算只依赖一次迭代读取的小批量序列(防止梯度计算开销太大)for s in state:s.detach_()inputs = to_onehot(X, vocab_size)# outputs有num_steps个形状为(batch_size, vocab_size)的矩阵(outputs, state) = rnn(inputs, state, params)# 拼接之后形状为(num_steps * batch_size, vocab_size)outputs = torch.cat(outputs, dim=0)# Y的形状是(batch_size, num_steps),转置后再变成长度为# batch * num_steps 的向量,这样跟输出的行一一对应y = torch.transpose(Y, 0, 1).contiguous().view(-1)# 使用交叉熵损失计算平均分类误差l = loss(outputs, y.long())# 梯度清0if params[0].grad is not None:for param in params:param.grad.data.zero_()l.backward()grad_clipping(params, clipping_theta, device)  # 裁剪梯度d2l.sgd(params, lr, 1)  # 因为误差已经取过均值,梯度不用再做平均l_sum += l.item() * y.shape[0]n += y.shape[0]if (epoch + 1) % pred_period == 0:print('epoch %d, perplexity %f, time %.2f sec' % (epoch + 1, math.exp(l_sum / n), time.time() - start))for prefix in prefixes:print(' -', predict_rnn(prefix, pred_len, rnn, params, init_rnn_state,num_hiddens, vocab_size, device, idx_to_char, char_to_idx))

在这里插入图片描述


8、A concise implementation of recurrent neural networks

import time
import math
import numpy as np
import torch
from torch import nn, optim
import torch.nn.functional as Fimport sys
sys.path.append("..") 
import d2lzh_pytorch as d2l
device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')(corpus_indices, char_to_idx, idx_to_char, vocab_size) = d2l.load_data_jay_lyrics()

8.1 definition model

PyTorch中的nn模块提供了循环神经网络的实现。下面构造一个含单隐藏层、隐藏单元个数为256的循环神经网络层rnn_layer

num_hiddens = 256
# rnn_layer = nn.LSTM(input_size=vocab_size, hidden_size=num_hiddens) # 已测试
rnn_layer = nn.RNN(input_size=vocab_size, hidden_size=num_hiddens)
num_steps = 35
batch_size = 2
state = None
X = torch.rand(num_steps, batch_size, vocab_size)
Y, state_new = rnn_layer(X, state)
print(Y.shape, len(state_new), state_new[0].shape)

输出:

torch.Size([35, 2, 256]) 1 torch.Size([2, 256])

如果rnn_layernn.LSTM实例,那么上面的输出是什么?

class RNNModel(nn.Module):def __init__(self, rnn_layer, vocab_size):super(RNNModel, self).__init__()self.rnn = rnn_layerself.hidden_size = rnn_layer.hidden_size * (2 if rnn_layer.bidirectional else 1) self.vocab_size = vocab_sizeself.dense = nn.Linear(self.hidden_size, vocab_size)self.state = Nonedef forward(self, inputs, state): # inputs: (batch, seq_len)# 获取one-hot向量表示X = d2l.to_onehot(inputs, self.vocab_size) # X是个listY, self.state = self.rnn(torch.stack(X), state)# 全连接层会首先将Y的形状变成(num_steps * batch_size, num_hiddens),它的输出# 形状为(num_steps * batch_size, vocab_size)output = self.dense(Y.view(-1, Y.shape[-1]))return output, self.state

8.2 training model

  • 定义一个预测函数。这里的实现区别在于前向计算和初始化隐藏状态的函数接口。
def predict_rnn_pytorch(prefix, num_chars, model, vocab_size, device, idx_to_char,char_to_idx):state = Noneoutput = [char_to_idx[prefix[0]]] # output会记录prefix加上输出for t in range(num_chars + len(prefix) - 1):X = torch.tensor([output[-1]], device=device).view(1, 1)if state is not None:if isinstance(state, tuple): # LSTM, state:(h, c)  state = (state[0].to(device), state[1].to(device))else:   state = state.to(device)(Y, state) = model(X, state)if t < len(prefix) - 1:output.append(char_to_idx[prefix[t + 1]])else:output.append(int(Y.argmax(dim=1).item()))return ''.join([idx_to_char[i] for i in output])

使用权重为随机值的模型来预测一次。

  1. 函数参数:

    • prefix: 预测的起始字符序列。
    • num_chars: 要预测的字符数量。
    • model: 已训练的 RNN 模型。
    • vocab_size: 词汇表的大小(字符的数量)。
    • device: 用于存储张量的设备(如 CPU 或 GPU)。
    • idx_to_char: 将索引映射到字符的字典。
    • char_to_idx: 将字符映射到索引的字典。
  2. 初始化:

    • state: 用于存储 RNN 的内部状态(如 LSTM 的隐藏状态和细胞状态)。
    • output: 初始化为 prefix 的第一个字符的索引。
  3. 预测循环:

    • 循环运行 num_chars + len(prefix) - 1 次。这是因为我们要先遍历 prefix 的所有字符(除了最后一个),然后再预测 num_chars 个字符。
    • X 是当前要输入到 RNN 的字符的索引(作为张量)。
    • 如果 state 不是 None,则将其移动到指定的 device 上。对于 LSTM,state 是一个元组,包含隐藏状态和细胞状态;对于其他 RNN 变体,它可能只是一个隐藏状态。
    • 使用 RNN 模型对 Xstate 进行预测,得到新的输出 Y 和新的 state
    • 如果当前字符是 prefix 的一部分(即 t < len(prefix) - 1),则将 prefix 的下一个字符的索引添加到 output 中。否则,从 Y 中选择概率最高的字符的索引,并将其添加到 output 中。
  4. 返回结果:

    • 使用 idx_to_char 字典将 output 中的索引转换回字符,并将这些字符连接成一个字符串返回。
model = RNNModel(rnn_layer, vocab_size).to(device)
predict_rnn_pytorch('分开', 10, model, vocab_size, device, idx_to_char, char_to_idx)

使用了相邻采样来读取数据。

def train_and_predict_rnn_pytorch(model, num_hiddens, vocab_size, device,corpus_indices, idx_to_char, char_to_idx,num_epochs, num_steps, lr, clipping_theta,batch_size, pred_period, pred_len, prefixes):loss = nn.CrossEntropyLoss()optimizer = torch.optim.Adam(model.parameters(), lr=lr)model.to(device)state = Nonefor epoch in range(num_epochs):l_sum, n, start = 0.0, 0, time.time()data_iter = d2l.data_iter_consecutive(corpus_indices, batch_size, num_steps, device) # 相邻采样for X, Y in data_iter:if state is not None:# 使用detach函数从计算图分离隐藏状态, 这是为了# 使模型参数的梯度计算只依赖一次迭代读取的小批量序列(防止梯度计算开销太大)if isinstance (state, tuple): # LSTM, state:(h, c)  state = (state[0].detach(), state[1].detach())else:   state = state.detach()(output, state) = model(X, state) # output: 形状为(num_steps * batch_size, vocab_size)# Y的形状是(batch_size, num_steps),转置后再变成长度为# batch * num_steps 的向量,这样跟输出的行一一对应y = torch.transpose(Y, 0, 1).contiguous().view(-1)l = loss(output, y.long())optimizer.zero_grad()l.backward()# 梯度裁剪d2l.grad_clipping(model.parameters(), clipping_theta, device)optimizer.step()l_sum += l.item() * y.shape[0]n += y.shape[0]try:perplexity = math.exp(l_sum / n)except OverflowError:perplexity = float('inf')if (epoch + 1) % pred_period == 0:print('epoch %d, perplexity %f, time %.2f sec' % (epoch + 1, perplexity, time.time() - start))for prefix in prefixes:print(' -', predict_rnn_pytorch(prefix, pred_len, model, vocab_size, device, idx_to_char,char_to_idx))
num_epochs, batch_size, lr, clipping_theta = 250, 32, 1e-3, 1e-2 # 注意这里的学习率设置
pred_period, pred_len, prefixes = 50, 50, ['分开', '不分开']
train_and_predict_rnn_pytorch(model, num_hiddens, vocab_size, device,corpus_indices, idx_to_char, char_to_idx,num_epochs, num_steps, lr, clipping_theta,batch_size, pred_period, pred_len, prefixes)

8.3 brief summary

  • PyTorch的nn模块提供了循环神经网络层的实现。
  • PyTorch的nn.RNN实例在前向计算后会分别返回输出和隐藏状态。该前向计算并不涉及输出层计算。

9、Kaggle house_price achieve

9.1 Implement the address

https://www.kaggle.com/competitions/california-house-prices

9.2 Implement screenshots

在这里插入图片描述

相关文章:

深度学习复盘与论文复现C

文章目录 4、Distributed training4.1 GPU architecture 5、Recurrent neural network5.1 The basic structure of RNN5.2 Neural networks without hidden states5.3 Recurrent neural networks with hidden states5.4 summary 6、Language Model Dataset (lyrics from Jay Ch…...

海洋日特别活动—深海来客——可燃冰

深海中有一种神奇的物质&#xff0c;似冰又不是冰。 别看它其貌不扬&#xff0c;但本领不小&#xff0c;遇火即燃&#xff0c;能量巨大&#xff0c;可谓是能源家族的新宠。它就是被国务院正式批准列为我国第173个矿种的“可燃冰”&#xff01; 可燃冰到底是个啥&#xff1f;它…...

Web前端放图片位置:深入探索与最佳实践

Web前端放图片位置&#xff1a;深入探索与最佳实践 在Web前端开发中&#xff0c;图片作为重要的视觉元素&#xff0c;其放置位置往往影响着网页的整体布局和用户体验。然而&#xff0c;如何合理地放置图片&#xff0c;以最大化其视觉效果并提升用户体验&#xff0c;却是一个颇…...

leetcode-02-[977]有序数组的平方[209]长度最小的子数组[59]螺旋矩阵II

一、[977]有序数组的平方 重点&#xff1a; 新引入一个数组&#xff0c;不要原数组操作 class Solution {public int[] sortedSquares(int[] nums) {int left0,right nums.length-1;int[] resultnew int[nums.length];int index nums.length-1;while(left<right){if(nums…...

Spring Cloud Gateway CORS 跨域方案

通过配置文件&#xff0c;以下配置就是其中一种方案。 gateway: #跨域配置globalcors: cors-configurations: [/**]: allowedMethods: "*"allowedHeaders: "*"allowedOriginPatterns: "*"allowCredentials: truedefault-filters: - DedupeRespo…...

高考后志愿填报信息采集系统制作指南

在高考的硝烟散去之后&#xff0c;每位学生都面临着一个重要的任务——志愿填报。老师们如何高效、准确地收集和整理这些信息&#xff0c;成为了一个棘手的问题。难道我们只能依赖传统的手工登记方式&#xff0c;忍受其繁琐和易错吗&#xff1f; 易查分是一个简单易用的在线工具…...

Python使用Flask构建简单的web应用

构建一个简单的 Flask Web 应用程序是学习 Python Web 开发的良好起点。Flask 是一个轻量级的 WSGI Web 应用框架&#xff0c;它的主要目标是让开发者更容易构建 Web 应用&#xff0c;同时保持简单性和灵活性。下面我们将详细介绍如何使用 Flask 构建一个简单的 Web 应用&#…...

看似不同的事情,却是相同的坑

目录 一、背景二、过程1.遭遇战-微盘股的下杀2.不失为一件好事3.一切向后看吧&#xff0c;最近的学习感受4.该有的心境 三、总结 一、背景 也在一点点改变&#xff0c;期间势必要经历流血的过程&#xff1b;所谓无疯狂不成长&#xff0c;积极的心态去应对&#xff0c;去总结总…...

在 Linux 系统上安装 Android NDK

在 Linux 系统上安装 Android NDK 1. Android NDK2. NDK Downloads2.1. Latest LTS Version (r26d)2.2. Old Unsupported Versions 3. 安装 NDK4. Get started with the NDK (NDK 使用入门)References 1. Android NDK https://developer.android.com/ndk The Android NDK is …...

SpringBoot的学习要点

目录 SpringBoot 创建项目 配置文件 注解 命名规范 SpringBoot整合第三方技术 …… 中文文档&#xff1a;Spring Boot 中文文档 SpringBoot Spring Boot 是基于 Spring 框架的一种快速构建微服务应用的方式它主要提供了自动配置、简化配置、运行时应用监控等功能它…...

vue3引入cesium和olcs

首先引入包 pnpm i olcs; pnpm i -D vite-plugin-cesium pnpm i -S cesium在vite.config.js中配置&#xff0c;参考这位大佬的笔记 添加链接描述 import { defineConfig } from vite import vue from vitejs/plugin-vue import cesium from vite-plugin-cesium; // https://…...

代码随想录算法训练营第25天|回溯

回溯part02 216. 组合总和 III /*** param {number} k* param {number} n* return {number[][]}*/ var combinationSum3 function(k, n) {// k个数字相加为n// 只能使用1-9// 每个数字只能使用一次// 不能重复 如 1 2 4 、 4 1 2 不可以let res [];backtracking(k, n, [], …...

Ajax 快速入门

Ajax 概念&#xff1a;Ajax是一种Web开发技术&#xff0c;允许在不重新加载整个页面的情况下&#xff0c;与服务器交换数据并更新网页的部分内容。 作用&#xff1a; 数据交换&#xff1a;Ajax允许通过JavaScript向服务器发送请求&#xff0c;并能够接收服务器响应的数据。 异…...

面试官:前端实现图片懒加载怎么做?这不是撞我怀里了嘛!

前端懒加载&#xff08;也称为延迟加载或按需加载&#xff09;是一种网页性能优化的技术&#xff0c;主要用于在网页中延迟加载某些资源&#xff0c;如图片、视频或其他媒体文件&#xff0c;直到它们实际需要被用户查看或交互时才进行加载。这种技术特别适用于长页面或包含大量…...

每天学习一个Windows命令或Linux命令——seq

今天我们来学习 seq命令&#xff01; seq命令&#xff08;单词sequence序列的缩写&#xff09;是Linux系统中用于输出序列化的一串整数的命令。 一、seq用法 seq用法一共有以下三种&#xff1a; seq [选项]... 尾数 seq [选项]... 首数 尾数 seq [选项]... 首数 增量&#…...

C++设计模式-中介者模式,游戏对象之间的碰撞检测

运行在VS2022&#xff0c;x86&#xff0c;Debug下。 31. 中介者模式 中介者模式允许对象之间通过一个中介者对象进行交互&#xff0c;而不是直接相互引用。可以减少对象之间的直接耦合&#xff0c;同时集中化管理复杂的交互。应用&#xff1a;如在游戏开发中&#xff0c;可以使…...

Rust-02-变量与可变性

在Rust中&#xff0c;变量和可变性是两个重要的概念。 变量&#xff1a;变量是用于存储数据的标识符。在Rust中&#xff0c;变量需要声明其类型&#xff0c;例如&#xff1a; let x: i32 5; // 声明一个名为x的变量&#xff0c;类型为i32&#xff08;整数&#xff09;&#…...

mov指令中不允许的操作——汇编语言

在 x86 汇编语言中&#xff0c;MOV 指令的限制有助于确保系统的稳定性和正确的操作。下面详细解释为什么这些操作是不允许的。 1. 段寄存器之间直接传送数据 MOV DS, ES ; 错误&#xff0c;不允许原因&#xff1a; 段寄存器是用来定义程序段的开始位置&#xff0c;如代码段…...

Python进阶-部署Flask项目(以TensorFlow图像识别项目WSGI方式启动为例)

本文详细介绍了如何通过WSGI方式部署一个基于TensorFlow图像识别的Flask项目。首先简要介绍了Flask框架的基本概念及其特点&#xff0c;其次详细阐述了Flask项目的部署流程&#xff0c;涵盖了服务器环境配置、Flask应用的创建与测试、WSGI服务器的安装与配置等内容。本文旨在帮…...

WooYun-2016-199433 -phpmyadmin-反序列化RCE-getshell

参考资料&#xff1a; Phpmyadmin 脚本/设置.php反序列化漏洞 &#xff08;WooYun-2016-199433&#xff09;复现_phpmyadmin scriptssetup.php 反序列化漏洞-CSDN博客 https://blog.csdn.net/haoxue__/article/details/129368455利用pearcmd.php文件包含拿shell&#xff08;L…...

社交“学习伙伴”:Meta Llama助力对话升级

每周跟踪AI热点新闻动向和震撼发展 想要探索生成式人工智能的前沿进展吗&#xff1f;订阅我们的简报&#xff0c;深入解析最新的技术突破、实际应用案例和未来的趋势。与全球数同行一同&#xff0c;从行业内部的深度分析和实用指南中受益。不要错过这个机会&#xff0c;成为AI领…...

LabVIEW 反向工程的实现与法律地位

什么是LabVIEW反向工程&#xff1f; 反向工程是指从现有的应用程序或软件中推导出其设计、架构、代码等信息的过程。对于LabVIEW而言&#xff0c;反向工程涉及从现有的VI&#xff08;虚拟仪器&#xff09;文件、项目或应用程序中提取出设计思路、功能模块、算法实现等。 LabV…...

Python怎么做单元测试

在Python中&#xff0c;最常用的单元测试框架是unittest。以下是如何使用unittest进行单元测试的步骤&#xff1a; 导入unittest模块&#xff1a; 首先&#xff0c;你需要导入unittest模块。 import unittest创建测试类&#xff1a; 你需要创建一个继承自unittest.TestCase的类…...

ghidra

https://github.com/NationalSecurityAgency/ghidra ghidra是一个so的逆向工具&#xff0c;功能和jadx-gui类似&#xff0c;但是和jadx-gui专注于java层的不同&#xff0c;ghidra专注于native层的代码反编译&#xff08;从二进制到c语言&#xff09;。 一、 安装 准备好java1…...

如何解决网络问题?

组织和 IT 管理员尽其所能完善他们的网络&#xff0c;但是&#xff0c;不同程度的网络问题仍然可能出现&#xff0c;这些网络问题需要立即响应和解决&#xff0c;如果这些问题在不合理的时间内得不到解决&#xff0c;网络和组织的损害可能会付出高昂的代价。这就是为什么 IT 管…...

高速USB转串口芯片CH343

CH343封装 截止目前&#xff0c;主要封装有 SOP16: CH343G QFN16: CH343P ESSOP10: CH343K,截止24年6月未生产 CH343串口速度 最高串口速度&#xff1a; 6Mbps,比CH340的2M&#xff0c;快3倍 1、概述 参考版本&#xff1a;1E CH343 是一个 USB 总线的转接芯片&#xff0c;…...

C++ MPI多进程并发

下载 用法 mpiexec -n 8 $PROCESS_COUNT x64\Debug\$TARGET.exe 多进程并发启动 mpiexec -f hosts.txt -n 3 $PROCESS_COUNT x64\Debug\$TARGET.exe 联机并发进程&#xff0c;其它联机电脑需在相同路径下有所有程序 //hosts.txt 192.168.86.16 192.168.86.123 192.168…...

UFS协议入门-分层结构

写在前面:本文参考UFS jedec3.1,本文思维导图如下 1. 分层概述 UFS协议分为3层,从上至下分别是:应用层(UAP),传输层(UTP),互联层(UIC),具体结构如下图所示。 2.1 应用层 在应用层(UAP)中,包括:UFS指令集(UCS),设备管理器(Device Manager),任务管理器(Task Manager…...

Docker Desktop - WSL distro terminated abruptly

打开 PowerShell 或以管理员身份运行的命令提示符。运行以下命令以列出已安装的 WSL 分发&#xff1a; wsl --list 运行以下命令以注销 Docker 相关的分发 wsl --unregister <distro_name> 将<distro_name>替换为实际的 Docker 相关分发的名称。将<distro_…...

HTML-CSS练习例子

HTML CSS 练习 https://icodethis.com 作为前端练习生。不敲代码只看&#xff0c;入门是很慢的&#xff0c;所以直接实战是学习前端最快的途径之一。 这个网站练习HTML CSS的&#xff0c;可以打开了解一下&#xff0c;可以每天打卡&#xff0c;例子简单&#xff0c;循序渐进&…...

怎么用dw制作网站/下载百度极速版免费安装

在我的一个网站上,我创建了一个表单,用于收集人员姓名,电子邮件和他们想法的描述.我将描述的字符限制为500个字符,因为我不想阅读,我想出了如何在用户输入他们想要的内容之前将文本显示在textarea中.目前用户必须自己删除“你的想法的描述”,但我想添加占位符类,当它们点击text…...

杭州市建设监理协会网站/北京优化靠谱的公司

结对项目——四则运算 阶段性总结 一、需求分析&#xff08;第一周达成&#xff09;&#xff1a; 能够生成n道四则运算题&#xff0c;n可由使用者输入来控制支持整数支持分数生成题目中含有括号可以判断正误&#xff0c;如果错误会输出正确答案统计正确率扩展需求&#xff1a; …...

做快餐 承包食堂的公司网站/搜索引擎环境优化

目录 事件绑定 1. 如何绑定事件 2. 获取事件对象 事件绑定 说明&#xff1a;能够独立绑定任何事件并能获取到事件对象e 1. 如何绑定事件 语法 on 事件名称 { 事件处理程序 } &#xff0c;比如&#xff1a;<div onClick{()>{}}></div> 注意点 react事件采…...

做网站政府扶持/自助建站系统

所以我有这个任务&#xff0c;我有一个问题&#xff0c;关于一个我不知道怎么做的部分&#xff0c;你们能帮我吗&#xff1f;def main():# Please see the commentslargest 0for index in range(3): # Enter the value(s) in the parenthesis to run the loop 3 timesnumber1 …...

以星空做的网站模板/大数据平台

程序员是一个十分特殊的群体&#xff0c; 他们十分内敛、腼腆&#xff0c; 但是只要一开口就能判断出来&#xff0c; 到底是在装逼还是有真本事&#xff0c; 不信咱们往下看&#xff01; 1、这个功能是小case&#xff0c;分分钟搞定 一般情况下&#xff0c;程序员在拿到一个新的…...

网站片头动画用什么软件做的/靖江seo要多少钱

我们在使用 vue element 写后台管理模板时&#xff0c;肯定逃不过左侧菜单也称侧边栏。举例&#xff1a;我们现在有一个 A 模块&#xff0c;A 模块中有详情页面和编辑页面【一共三个页面】&#xff0c;我们通常怎么考虑&#xff1f;将三个页面写在一个 vue 里&#xff0c;通过…...