2-11 演示:使用Pytorch训练模型

目录

1 深度学习训练模型步骤:

2 训练目的:

3 训练步骤:

3.1 准备训练数据:

3.2 main.py:

3.2.1 目的:

3.2.2 parser模块:

3.2.3 指定采用pytroch进行训练:

3.2.4 数据ID化(Corpus类):

3.2.5 对数据进行分批处理,并构建模型和损失函数:

3.2.6 train函数(训练模型时的配置):

​3.2.7 训练过程的控制流程:

3.2.8 完整代码:

3.3 data.py:

3.3. 1 目的:

3.3.2 完整代码:

3.4 model.py:

3.4.1 目的:

3.4.2 具体步骤:

3.4.3 完整代码:

4 模型训练结果:

5 其他优秀pytroch训练项目:


1 深度学习训练模型步骤:

任何一个深度学习训练模型的流程都要按照如下步骤:

  1. 准备数据;
  2. 建立模型;
  3. 训练模型;
  4. 评估模型;
  5. 测试模型;
  6. 改进模型

2 训练目的:

使用PyTorch训练一个语言模型,目标是根据上文预测下一个单词,并在训练过程中评估模型在验证集和测试集上的性能。

我们选取的模式是LSTM模型,为了求得目标值和预测值之间的差距,我们选取Cross-entropy(交叉熵)作为损失函数。

3 训练步骤:

3.1 准备训练数据:

我们选取的训练数据来源于“维基百科”,并将其分割为训练集、验证集以及测试集。

3.2 main.py:
3.2.1 目的:

主函数,训练入口

3.2.2 parser模块:

简单理解就是为了进行全局参数配置。

argparse 模块是 Python 内置的一个用于命令项选项与参数解析的模块,argparse 模块可以让人轻松编写用户友好的命令行接口。通过在程序中定义好我们需要的参数,然后 argparse 将会从 sys.argv 解析出这些参数。argparse 模块还会自动生成帮助和使用手册,并在用户给程序传入无效参数时报出错误信息。

想要深入了解可以参考下面这个博客;

【精选】python之parser.add_argument()用法——命令行选项、参数和子命令解析器_parser.add_argument()参数_python-码博士的博客-CSDN博客

3.2.3 指定采用pytroch进行训练:

3.2.4 数据ID化(Corpus类):

(1)构建一个语料库对象,其中包括一个词典和经过ID化处理的训练集、验证集和测试集数据:

(2)具体来讲,Corpus的具体操作是将文本文件进行分词和标记化处理:

  1. 首先,通过assert os.path.exists(path)进行断言,确保指定的文件路径存在。
  2. 接下来,通过打开文件并使用with语句来读取文件内容。对于文件中的每一行,将其拆分成单词,并在末尾添加'<eos>',表示句子的结束。
  3. 然后,对于每个单词,调用self.dictionary.add_word(word)方法将其添加到语料库的词典中。如果词典中已经存在该单词,就直接获取其对应的ID,如果词典中不存在该单词,就将该单词添加到词典中,并分配一个新的ID给它。
  4. 接下来,重新打开文件,再次遍历每一行。对于每个单词,使用self.dictionary.word2idx[word]来获取其在词典中对应的ID,并将这些ID存储在ids列表中。
  5. 最后,将所有行的ID组成的列表idss使用torch.cat方法连接起来,并将结果存储在ids变量中。这样,ids就是整个文本文件中所有单词对应的ID序列。

3.2.5 对数据进行分批处理,并构建模型和损失函数:

(1)batchify的样式转化图:

如下图所示,这里的bsz就是4(也就是分为4组),每一列表示一组连续的数据。

(2)具体步骤:

  1. 函数接收两个参数,databszdata是一个包含整型数据的张量,bsz表示每个批次的大小。
  2. 首先,通过计算data.size(0) // bsz,得到可以将数据集等分为多少个批次。
  3. 接下来,使用data.narrow(0, 0, nbatch * bsz)将数据集截取为能够整除bsz的大小。
  4. 然后,通过data.view(bsz, -1).t().contiguous()将数据重新组织为以bsz为行数的矩阵,并进行内存连续化处理。
  5. 最后,将处理后的数据转移到设备上,并将其作为函数的返回值。
  6. 在主代码中,使用batchify函数将训练集、验证集和测试集数据进行分批处理,并分别存储在train_dataval_datatest_data变量中。
  7. 接下来,使用len(corpus.dictionary)获取词典中的词语数量,用于构建模型。
  8. 如果args.model的值是'LSTM',则使用model.RNNModel构造一个RNN模型,并将模型移动到设备上。(模型细节请参考下文的model.py)
  9. 然后,使用nn.CrossEntropyLoss()构建交叉熵损失函数,该函数用于计算训练的目标值。

3.2.6 train函数(训练模型时的配置):
  1. 在函数中,首先将模型设置为训练模式(model.train()),然后初始化一些变量,如总损失(total_loss)和起始时间(start_time)。
  2. 接下来,通过调用model.init_hidden(args.batch_size)初始化隐藏状态(hidden)(这个是因为我们在第一次训练时没有前一个hidden层,因此要全部设为0作为初始状态)。在每个批次的训练过程中,通过调用get_batch(train_data, i)获取输入数据(data)和目标数据(targets)。
  3. 然后,通过调用model.zero_grad()将模型的梯度置零,再通过调用repackage_hidden(hidden)对隐藏状态进行处理,这是为了防止梯度回传过程中的梯度累积问题。
  4. 接下来,通过调用model(data, hidden)进行前向传播计算,得到模型的输出(output)和最新的隐藏状态(hidden)。
  5. 然后,通过调用criterion(output, targets)计算输出和目标之间的损失,并调用loss.backward()进行反向传播计算梯度。
  6. 接下来,通过调用torch.nn.utils.clip_grad_norm_(model.parameters(), args.clip)对梯度进行裁剪,以防止梯度爆炸的问题。
  7. 然后,使用手动更新参数的方式,通过循环遍历模型的参数,并调用p.data.add_(p.grad, alpha=-lr)来更新参数。
  8. 最后,统计损失值,并在每个一定的批次数(args.log_interval)之后打印出当前训练的进度,包括当前的损失值(cur_loss)和困惑度(math.exp(cur_loss))。
  9. 整个训练过程会持续多个周期(epoch),并在每个周期结束后,根据验证集上的损失情况判断是否达到了最佳的验证损失值(best_val_loss)。如果验证损失值最佳,则将当前模型保存为最佳模型。
3.2.7 训练过程的控制流程:

整个训练过程中,会打印出训练过程的详细信息,包括每个周期的训练和验证损失值,以及最终的测试损失值和测试困惑度。

  1. 首先,通过一个循环迭代训练多个周期(args.epochs)。在每个周期中,记录当前周期的起始时间(epoch_start_time),然后调用train()函数进行模型训练。
  2. 接下来,通过调用evaluate(val_data)在验证集上进行模型评估,得到验证损失值(val_loss)。然后打印出当前周期的信息,包括周期数(epoch)、训练时间、验证损失值和验证困惑度。
  3. 然后,判断当前的验证损失值是否是最佳的验证损失值(best_val_loss)。如果是,则将当前模型保存为最佳模型。
  4. 如果当前的验证损失值没有达到最佳值,则通过将学习率(lr)除以4.0来衰减学习率。
  5. 接下来,通过使用torch.load加载保存的最佳模型,并使用flatten_parameters函数将RNN模型的参数变为连续的内存块,以加快前向传播的速度。

  6. 然后,通过调用evaluate(test_data)在测试集上进行模型评估,得到测试损失值(test_loss)。然后打印出最终的训练结果,包括测试损失值和测试困惑度。

3.2.8 完整代码:
# coding: utf-8
# 训练的入口
import argparse
import time
import math
import os
import torch
import torch.nn as nn

import data
import model

#通过parser来传入一些输入的参数
parser = argparse.ArgumentParser(description='PyTorch Wikitext-2 RNN/LSTM/GRU/Transformer Language Model')
parser.add_argument('--data', type=str, default='./data/wikitext-2',
                    help='location of the data corpus')
parser.add_argument('--model', type=str, default='LSTM',
                    help='type of network (RNN_TANH, RNN_RELU, LSTM, GRU, Transformer)')
parser.add_argument('--emsize', type=int, default=200,
                    help='size of word embeddings')
parser.add_argument('--nhid', type=int, default=200,
                    help='number of hidden units per layer')
parser.add_argument('--nlayers', type=int, default=2,
                    help='number of layers')
parser.add_argument('--lr', type=float, default=20,  # 你可能需要调整它
                    help='initial learning rate')
parser.add_argument('--clip', type=float, default=0.25,
                    help='gradient clipping')
parser.add_argument('--epochs', type=int, default=40,
                    help='upper epoch limit')
parser.add_argument('--batch_size', type=int, default=20, metavar='N',
                    help='batch size')
parser.add_argument('--bptt', type=int, default=35,
                    help='sequence length')
parser.add_argument('--dropout', type=float, default=0.2,
                    help='dropout applied to layers (0 = no dropout)')
parser.add_argument('--tied', action='store_true',
                    help='tie the word embedding and softmax weights')
parser.add_argument('--seed', type=int, default=1111,
                    help='random seed')
parser.add_argument('--cuda', action='store_true',
                    help='use CUDA')
parser.add_argument('--log-interval', type=int, default=200, metavar='N',
                    help='report interval')
parser.add_argument('--save', type=str, default='model.pt',
                    help='path to save the final model')
parser.add_argument('--nhead', type=int, default=2,
                    help='the number of heads in the encoder/decoder of the transformer model')
parser.add_argument('--dry-run', action='store_true',
                    help='verify the code and the model')
args = parser.parse_args()

#采用torch
# Set the random seed manually for reproducibility.
torch.manual_seed(args.seed)
if torch.cuda.is_available():
    if not args.cuda:
        print("WARNING: You have a CUDA device, so you should probably run with --cuda.")

device = torch.device("cuda" if args.cuda else "cpu")

# 和word_language_model不同, sst2的corpus既要包含输入也要包含输出(label)
corpus = data.Corpus(args.data)

# Starting from sequential data, batchify arranges the dataset into columns.
# For instance, with the alphabet as the sequence and batch size 4, we'd get
# ┌ a g m s ┐
# │ b h n t │
# │ c i o u │
# │ d j p v │
# │ e k q w │
# └ f l r x ┘.
# These columns are treated as independent by the model, which means that the
# dependence of e. g. 'g' on 'f' can not be learned, but allows more efficient
# batch processing.

###############################################################################
# sst2改动batchify功能: 输出是一个数组,每个元素代表一个batch,每个batch是一个pair,包含data和label, data的shape为[seq_len, bsz](可由[bsz, seq_len]转置得到),label是一维0/1数组
###############################################################################
#batchify函数将文本数据映射成连续数字, 并转换成指定的样式,bsz是就是batch_size, 每次模型更新参数的数据量
def batchify(data, bsz):
    # Work out how cleanly we can divide the dataset into bsz parts.
    nbatch = data.size(0) // bsz
    # Trim off any extra elements that wouldn't cleanly fit (remainders).
    data = data.narrow(0, 0, nbatch * bsz)
    # Evenly divide the data across the bsz batches.
    data = data.view(bsz, -1).t().contiguous()
    return data.to(device)

eval_batch_size = 10
train_data = batchify(corpus.train, args.batch_size)
val_data = batchify(corpus.valid, eval_batch_size)
test_data = batchify(corpus.test, eval_batch_size)

ntokens = len(corpus.dictionary)
if args.model == 'LSTM':#构建模型
    model = model.RNNModel(args.model, ntokens, args.emsize, args.nhid, args.nlayers, args.dropout, args.tied).to(device)

criterion = nn.CrossEntropyLoss()#构建训练目标,我们的目标是使得这个损失函数的值尽可能得小

def repackage_hidden(h):
    """Wraps hidden states in new Tensors, to detach them from their history."""

    if isinstance(h, torch.Tensor):
        return h.detach()
    else:
        return tuple(repackage_hidden(v) for v in h)


# get_batch subdivides the source data into chunks of length args.bptt.
# If source is equal to the example output of the batchify function, with
# a bptt-limit of 2, we'd get the following two Variables for i = 0:
# ┌ a g m s ┐ ┌ b h n t ┐
# └ b h n t ┘ └ c i o u ┘
# Note that despite the name of the function, the subdivison of data is not
# done along the batch dimension (i.e. dimension 1), since that was handled
# by the batchify function. The chunks are along dimension 0, corresponding
# to the seq_len dimension in the LSTM.

# 这个函数可能不再需要了,因为经过batchify后,我们的数据已经处理成一个batch一个batch的数组了,直接取就可以了
def get_batch(source, i):
    seq_len = min(args.bptt, len(source) - 1 - i)
    data = source[i:i+seq_len]
    target = source[i+1:i+1+seq_len].view(-1)
    return data, target


def evaluate(data_source):  # 参考下面的train函数更改
    # Turn on evaluation mode which disables dropout.
    model.eval()
    total_loss = 0.
    ntokens = len(corpus.dictionary)
    hidden = model.init_hidden(eval_batch_size)
    with torch.no_grad():#为了防止评测过程中产生任何梯度,只希望去使用这组参数去做评测,而不对参数进行更新修改
        for i in range(0, data_source.size(0) - 1, args.bptt):
            data, targets = get_batch(data_source, i)
            output, hidden = model(data, hidden)#前向传递和训练过程中的一致
            hidden = repackage_hidden(hidden)
            total_loss += len(data) * criterion(output, targets).item()
            ###############################################################################
            # sst2不应只得到loss, 还要评测label正确的概率, 即acc
            ###############################################################################
            # 怎么得到预测结果呢?
            # 提示:pred = output.argmax(dim=1)
    return total_loss / (len(data_source) - 1)


def train():
    # Turn on training mode which enables dropout.
    model.train()
    total_loss = 0.
    start_time = time.time()
    ntokens = len(corpus.dictionary)
    hidden = model.init_hidden(args.batch_size)  # 还需要吗?#由于第一次训练没有上一个hidden值,因此这里我们就将其初始化全零
    for batch, i in enumerate(range(0, train_data.size(0) - 1, args.bptt)):  # 由于我们已经把train_data整理成batch构成的数组了,这里直接进行迭代即可。
        data, targets = get_batch(train_data, i)#构建data(输入)和target(输出)
        # Starting each batch, we detach the hidden state from how it was previously produced.
        # If we didn't, the model would try backpropagating all the way to start of the dataset.
        model.zero_grad()#训练前将模型的梯度置0
        hidden = repackage_hidden(hidden)  # 还需要吗?为什么在LM任务中需要它?#每一次训练都需要用到上一步得hidden
        ###############################################################################
        # sst2的output应为0/1二分类的概率(经过softmax之前)
        ###############################################################################
        output, hidden = model(data, hidden)  # 作出相应更改#因此就将data和hidden都输入到模型中,开始进入forward计算
        loss = criterion(output, targets)  # 这里需要改吗?softmax在哪里执行的?#计算output和目标值之间的loss
        loss.backward() #开始反向传播,得到每一组参数的梯度,需要观察grade是否是正常的
        # `clip_grad_norm` helps prevent the exploding gradient problem in RNNs / LSTMs.
        torch.nn.utils.clip_grad_norm_(model.parameters(), args.clip)#为了防止梯度爆炸,将梯度超过clip的部分截掉
        
        # 这里展示的是手动更新参数的方式,而不是使用optimizer。你可以试一试改成optimizer。
        for p in model.parameters():
            p.data.add_(p.grad, alpha=-lr)#更新参数
            
        total_loss += loss.item()

        if batch % args.log_interval == 0 and batch > 0:
            cur_loss = total_loss / args.log_interval
            elapsed = time.time() - start_time
            print('| epoch {:3d} | {:5d}/{:5d} batches | lr {:02.2f} | ms/batch {:5.2f} | '
                    'loss {:5.2f} | ppl {:8.2f}'.format(
                epoch, batch, len(train_data) // args.bptt, lr,
                elapsed * 1000 / args.log_interval, cur_loss, math.exp(cur_loss)))  # 去掉perplexity
            total_loss = 0
            start_time = time.time()
        if args.dry_run:
            break

# Loop over epochs.
lr = args.lr
best_val_loss = None

# At any point you can hit Ctrl + C to break out of training early.

try:
    for epoch in range(1, args.epochs+1):#训练次数
        epoch_start_time = time.time()
        train()
        val_loss = evaluate(val_data)#在验证集上进行验证        print('-' * 89)
        print('| end of epoch {:3d} | time: {:5.2f}s | valid loss {:5.2f} | '
                'valid ppl {:8.2f}'.format(epoch, (time.time() - epoch_start_time),
                                           val_loss, math.exp(val_loss)))
        print('-' * 89)
        # Save the model if the validation loss is the best we've seen so far.
        if not best_val_loss or val_loss < best_val_loss:
            with open(args.save, 'wb') as f:
                torch.save(model, f)
            best_val_loss = val_loss#找出最优的模型参数配比,而非最后一个
        else:
            # Anneal the learning rate if no improvement has been seen in the validation dataset.
            lr /= 4.0
except KeyboardInterrupt:
    print('-' * 89)
    print('Exiting from training early')


# Load the best saved model.
with open(args.save, 'rb') as f:
    model = torch.load(f)
    # after load the rnn params are not a continuous chunk of memory
    # this makes them a continuous chunk, and will speed up forward pass
    # Currently, only rnn model supports flatten_parameters function.
    if args.model in ['RNN_TANH', 'RNN_RELU', 'LSTM', 'GRU']:
        model.rnn.flatten_parameters()

# Run on test data.
test_loss = evaluate(test_data)
print('=' * 89)
print('| End of training | test loss {:5.2f} | test ppl {:8.2f}'.format(
    test_loss, math.exp(test_loss)))
print('=' * 89)

3.3 data.py:
3.3. 1 目的:

预处理数据,来构建一个词典,并将单词ID化,得到ID化的三个数据集。主要的Corpus类主要用于将输入数据ID化。

3.3.2 完整代码:
# 预处理数据,来构建一个词典,并将单词ID化,得到ID化的三个数据集
import os
from io import open
import torch

class Dictionary(object):
    def __init__(self):
        self.word2idx = {}
        self.idx2word = []

    def add_word(self, word):
        if word not in self.word2idx:
            self.idx2word.append(word)
            self.word2idx[word] = len(self.idx2word) - 1
        return self.word2idx[word]

    def __len__(self):
        return len(self.idx2word)


class Corpus(object):
    def __init__(self, path):#包括一个词典和ID化后的三个数据集,词典中的每一个词语对应一个ID
        self.dictionary = Dictionary()
        # 添加一个参数,记录设置的最大序列长度
        self.train = self.tokenize(os.path.join(path, r'F:\学习笔记\NLP\Pipeline_pytorch\data\wikitext\train.txt'))  # 改成相应的文件名
        self.valid = self.tokenize(os.path.join(path, r'F:\学习笔记\NLP\Pipeline_pytorch\data\wikitext\valid.txt'))
        self.test = self.tokenize(os.path.join(path, r'F:\学习笔记\NLP\Pipeline_pytorch\data\wikitext\test.txt'))

    def tokenize(self, path):
        """Tokenizes a text file."""
        assert os.path.exists(path)
        # Add words to the dictionary
        # self.dictionary.add_word('<pad>')  # 用于padding
        with open(path, 'r', encoding="utf8") as f:
            for line in f:
                words = line.split() + ['<eos>']  # 不再需要<eos>了。为什么?<eos>在LM任务中起到什么作用?
                for word in words:
                    self.dictionary.add_word(word)#遇到一个词典,词典中没有就添加到词典中,若出现过,就直接获取其ID

        # Tokenize file content
        with open(path, 'r', encoding="utf8") as f:
            idss = []
            ###############################################################################
            # sst2按行构建输入, 长于seq_len的句子进行截断,短于seq_len的用<pad>补齐长度至seq_len
            ###############################################################################
            for line in f:
                words = line.split() + ['<eos>']#终止符
                ids = []
                for word in words:
                    ids.append(self.dictionary.word2idx[word])
                idss.append(torch.tensor(ids).type(torch.int64))
            ids = torch.cat(idss)

            ###############################################################################
            # 构建输出, sst2的label取出, positive的label为1, negative的label为0, 返回格式为多行(ids, label)
            ###############################################################################
        
        ###############################################################################
        # sst2语料库 
        # The dinner is great. \t positive
        # I hate summer. \t negative
        # ...
        # sst2返回格式, 其中pad_id=0
        # (torch.tensor[9,6,7,4,0,0,...], 1)
        # (torch.tensor[3,2,8,0,0,0,...], 0)
        # ...
        ###############################################################################
        return ids
3.4 model.py:
3.4.1 目的:

一个封装好的LSTM模型,其中主要定义了RNNModel类。

3.4.2 具体步骤:
  1. 这段代码定义了一个名为RNNModel的LSTM模型类,该类继承自nn.Module
  2. 模型类包含了一个嵌入层(self.encoder)(用于将ID向量转换为词向量)、一个循环层(self.rnn)(直接调用troch中的LSTM即可)和一个线性层(self.decoder)(将hidden维度的向量转换为词典长度维度的向量)。
  3. __init__方法中,模型类接收一些参数,包括循环层的类型(rnn_type),词汇表的大小(ntoken),嵌入层的维度(ninp),隐藏层的维度(nhid),循环层的层数(nlayers),以及一些可选的参数如dropout(防止过拟合)tied_weights等。
  4. 在模型的前向传播(forward)方法中:首先将输入的ID序列通过嵌入层转换为词向量(emb);然后将词向量输入到循环层(self.rnn)中进行训练;循环层的输出(output)经过dropout层(self.drop)处理后;再经过线性层(self.decoder)得到词表上的概率分布(decoded)。
  5. 模型还定义了一个init_hidden方法,用于初始化隐藏状态(hidden),并返回一个与输入批次大小(bsz)相匹配的零张量。
  6. 整体上,这个RNNModel类封装了一个LSTM模型的结构和计算过程,可以用于文本分类或其他相关任务。
3.4.3 完整代码:
#封装好的LSTM模型
import math
import torch
import torch.nn as nn
import torch.nn.functional as F

class RNNModel(nn.Module):
    """Container module with an encoder, a recurrent module, and a decoder."""

    def __init__(self, rnn_type, ntoken, ninp, nhid, nlayers, dropout=0.5, tie_weights=False):
        super(RNNModel, self).__init__()
        self.ntoken = ntoken
        self.encoder = nn.Embedding(ntoken, ninp)#:负责将输入(Input)转化为特征(Feature)||向量
        if rnn_type in ['LSTM', 'GRU']:#直接调用torch中封装好的LSTM层
            self.rnn = getattr(nn, rnn_type)(ninp, nhid, nlayers, dropout=dropout)
        else:
            try:
                nonlinearity = {'RNN_TANH': 'tanh', 'RNN_RELU': 'relu'}[rnn_type]
            except KeyError:
                raise ValueError( """An invalid option for `--model` was supplied,
                                 options are ['LSTM', 'GRU', 'RNN_TANH' or 'RNN_RELU']""")
            self.rnn = nn.RNN(ninp, nhid, nlayers, nonlinearity=nonlinearity, dropout=dropout)
        ###############################################################################
        # sst2的decoder不应在整个词表上预测,而是预测0/1的概率,因此下面ntoken可能需要改成2,即2分类
        ###############################################################################
        self.decoder = nn.Linear(nhid, ntoken)#Decoder类接收Encoder类的实现,Decoder类接收Encoder类输出的h,输出目标字符串,
        self.drop = nn.Dropout(dropout)#防止过拟合

        self.init_weights()

        self.rnn_type = rnn_type
        self.nhid = nhid
        self.nlayers = nlayers

    def init_weights(self):#初始化模型之后,对其结构进行初始化操作
        initrange = 0.1
        nn.init.uniform_(self.encoder.weight, -initrange, initrange)
        nn.init.zeros_(self.decoder.bias)
        nn.init.uniform_(self.decoder.weight, -initrange, initrange)

    #进行前向传播计算
    #第一步:将输入得ID转换为对应的词向量
    #第二步:输入到LSTM模型中进行训练
    #第三步:输出词表上的概率分布(但是首先得到的一个hidden维度的)
    #第四步:通过decodeer经过线性层得到一个词表维度的概率分布
    def forward(self, input, hidden):  # hidden在这里面没用了,我们不需要上一个状态的hidden,每个样本都是独立的
        emb = self.drop(self.encoder(input))
        output, hidden = self.rnn(emb, hidden)  # 这里面也不需要hidden作为输入了,输出也不用要hidden
        output = self.drop(output)  # shape是seq_len, bsz, nhid#防止过拟合

        # 补充代码,从上面的output中,抽取最后一个词的输出作为最终输出。要注意考虑到序列的真实长度。最后得到一个shape是bsz, nhid的tensor
        # 提示:output = output[real_seq_lens - 1, torch.arange(output.shape[1]), :]

        decoded = self.decoder(output)
        decoded = decoded.view(-1, self.ntoken)#为了和目标值target维度保持一致,把bath维度和句子维度压到一个维度里
        return decoded, hidden  # 不再需要输出hidden;最终输出的shape是bsz, 2

    def init_hidden(self, bsz):
        weight = next(self.parameters())
        if self.rnn_type == 'LSTM':
            return (weight.new_zeros(self.nlayers, bsz, self.nhid),
                    weight.new_zeros(self.nlayers, bsz, self.nhid))
        else:
            return weight.new_zeros(self.nlayers, bsz, self.nhid)

4 模型训练结果:

可以看到无论是训练集还是验证集,它的loss和ppl都是逐渐下降的

5 其他优秀pytroch训练项目:

由于本节课的讲解过于模糊,因此如果想要深入学习可以参考下面这篇博客:

Pytorch LSTM实现中文单词预测(附完整训练代码)_ngram模型代码-CSDN博客

但是苦于没有源码,不过倒是有比较清晰完善的思路,值得一看。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值