原始数据目录

Q1RNN实现古诗句生成任务

import os
import numpy as np
import pickle
import torch
import torch.nn as nn
from gensim.models.word2vec import Word2Vec
from torch.utils.data import Dataset, DataLoader


def split_text(file="poetry_7.txt", train_num=6000):
    #读取数据
    all_data = open(file, "r", encoding="utf-8").read()
    with open("split_7.txt", "w", encoding="utf-8") as f:
        split_data = " ".join(all_data)
        f.write(split_data)
    #返回处理后字符串的前 train_num * 64个字符
    return split_data[: train_num * 64]

def train_vec(vector_size=128, split_file="split_7.txt", org_file="poetry_7.txt", train_num=6000):
    param_file = "word_vec.pkl"
    #读取原始数据
    org_data = open(org_file, "r", encoding="utf-8").read().split("\n")[:train_num]
    #检查是否存在已经分割的文件
    #如果存在则读取部分数据
    if os.path.exists(split_file):
        all_data_split = (open(split_file, "r", encoding="utf-8").read().split("\n")[:train_num])
    else:
    #如果不存在则调用split_text函数分割数据
        all_data_split = split_text().split("\n")[:train_num]
    #检查模型参数
    if os.path.exists(param_file):
        return org_data, pickle.load(open(param_file, "rb"))
    #使用Word2vec训练模型
    models = Word2Vec(all_data_split, vector_size=vector_size, workers=7, min_count=1)
    #保存模型参数到文件中
    pickle.dump([models.syn1neg, models.wv.key_to_index, models.wv.index_to_key],open(param_file, "wb"),)
    #返回原始数据和训练好的模型参数
    return org_data, (models.syn1neg, models.wv.key_to_index, models.wv.index_to_key)

class Poetry_Dataset(Dataset):
    def __init__(self, w1, word_2_index, all_data):
        self.w1 = w1 #词向量矩阵
        self.word_2_index = word_2_index #单词到索引的映射字典
        self.all_data = all_data #所有诗歌数据

    def __getitem__(self, index):
        a_poetry = self.all_data[index] #获得一个诗歌数据
        a_poetry_index = [self.word_2_index[i] for i in a_poetry] #将诗歌中的单词转换为对应的索引
        xs = a_poetry_index[:-1] #输入序列
        ys = a_poetry_index[1:]  #目标序列
        xs_embedding = self.w1[xs] #获取输入序列的词向量表示
        #返回输入序列的词向量表示和目标序列的索引表示
        return xs_embedding, np.array(ys).astype(np.int64)

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

### 实验要求:

1. 在Poetry_Model_lstm的to_train函数中,实现:

- loss计算

- 反向传播

- 优化器更新

2. 完成generate_poetry_acrostic函数中缺少的代码,并任意生成一首藏头诗

class Poetry_Model_lstm(nn.Module):
    def __init__(self, params):
        super().__init__()
        #调用train_vec函数加载数据和词向量模型参数
        self.all_data, (self.w1, self.word_2_index, self.index_2_word) = train_vec(
            vector_size=params["embedding_num"], train_num=params["train_num"])
        #设置模型配置
        self.device="cpu" #训练设备CPU
        self.hidden_num = params["hidden_num"] #LSTM隐藏层大小
        self.batch_size = params["batch_size"] #批量大小
        self.epochs = params["epochs"] #轮数
        self.lr = params["lr"] #学习率
        self.optimizer = params["optimizer"] #优化器选择
        self.word_size, self.embedding_num = self.w1.shape #词向量维度
        #搭建模型结构
        self.lstm = nn.LSTM(
            input_size=self.embedding_num, #输入大小为词向量维度
            hidden_size=self.hidden_num, #LSTM隐藏层大小
            batch_first=True,#输入数据的第一个维度是batch_size
            num_layers=2, #LSTM层数
            bidirectional=False,) #不适用双向LSTM
        self.dropout = nn.Dropout(0.3)  # 古诗不具有唯一性,防止过拟合
        self.flatten = nn.Flatten(0, 1) #展平操作,将多维输入拉平成一维
        self.linear = nn.Linear(self.hidden_num, self.word_size) #全连接层,将LSTM输出映射到词汇空间
        self.cross_entropy = nn.CrossEntropyLoss()#交叉熵损失函数
    #前向传播
    def forward(self, xs_embedding, h_0=None, c_0=None):
        #如果没有提供初始隐藏状态和记忆状态,则创建全0的状态张量
        if h_0 == None or c_0 == None:
            h_0 = torch.tensor(
                np.zeros((2, xs_embedding.shape[0], self.hidden_num), dtype=np.float32))
            c_0 = torch.tensor(
                np.zeros((2, xs_embedding.shape[0], self.hidden_num), dtype=np.float32))
        #将隐藏状态和记忆状态转移到CPU
        h_0 = h_0.to(self.device)
        c_0 = c_0.to(self.device)
        #输入数据转移到CPU
        xs_embedding = xs_embedding.to(self.device)
        #LSTM层向前传播
        hidden, (h_0, c_0) = self.lstm(xs_embedding, (h_0, c_0))#LSTM层的输出和更新的隐藏状态、记忆状态
        hidden_drop = self.dropout(hidden) #应用Dropout层,防止过你哈
        hidden_flatten = self.flatten(hidden_drop)#多维数据展平成一维
        pre = self.linear(hidden_flatten)#全连接层
        return pre, (h_0, c_0) #返回预测结果和更新后的隐藏状态和记忆状态
    #训练
    def to_train(self):
        model_result_file = "Poetry_Model_lstm_model.pkl"
        if os.path.exists(model_result_file):
            return pickle.load(open(model_result_file, "rb"))
        #加载数据集
        dataset = Poetry_Dataset(self.w1, self.word_2_index, self.all_data)
        dataloader = DataLoader(dataset, self.batch_size)
        #优化器
        optimizer = self.optimizer(self.parameters(), self.lr)
        #将模型放到CPU
        self = self.to(self.device)
        #循环训练模型多个epoch
        for e in range(self.epochs):
            #遍历数据集的每个批次进行训练
            for batch_index, (batch_x_embedding, batch_y_index) in enumerate(dataloader):
                self.train() #开启训练模式
                #将批次数据转移到CPU上
                batch_x_embedding = batch_x_embedding.to(self.device)
                #将目标序列索引转移到CPU
                batch_y_index = batch_y_index.to(self.device)
                #根据输入词向量,预测结果
                pre, _ = self(batch_x_embedding)

                # ======================
                # 在此行注释下完成相应内容
                #计算损失
                loss = self.cross_entropy(pre.view(-1,self.word_size),batch_y_index.view(-1))
                #优化器梯度归零
                optimizer.zero_grad()
                #反向传播计算损失
                loss.backward()
                #更新参数
                optimizer.step()
                # ======================

                if batch_index % 100 == 0:
                    print(f"loss:{loss:.3f}")
                    #生成自动诗句
                    self.generate_poetry_auto()
        #保存模型参数             
        pickle.dump(self, open(model_result_file, "wb"))
        return self
    #自动生成自由诗
    def generate_poetry_auto(self):
        result = ""
        word_index = np.random.randint(0, self.word_size, 1)[0]

        result += self.index_2_word[word_index]
        h_0 = torch.tensor(np.zeros((2, 1, self.hidden_num), dtype=np.float32))
        c_0 = torch.tensor(np.zeros((2, 1, self.hidden_num), dtype=np.float32))

        for i in range(31):
            word_embedding = torch.tensor(self.w1[word_index][None][None])
            pre, (h_0, c_0) = self(word_embedding, h_0, c_0)
            word_index = int(torch.argmax(pre))
            result += self.index_2_word[word_index]

        print(result)
    #自动生成藏头诗
    def generate_poetry_acrostic(self):
        input_text = input("请输入四个汉字:")[:4]
        if input_text == "":
            self.generate_poetry_auto()
        else:
            result = ""
            punctuation_list = [",", "。", ",", "。"]
            for i in range(4):
                h_0 = torch.tensor(
                    np.zeros((2, 1, self.hidden_num), dtype=np.float32))
                c_0 = torch.tensor(
                    np.zeros((2, 1, self.hidden_num), dtype=np.float32))
                word = input_text[i]
                try:
                    # ======================
                    word_index = self.word_2_index[word]
                    # ======================
                except:
                    word_index = np.random.randint(0, self.word_size, 1)[0]
                    word = self.index_2_word[word_index]
                result += word

                for j in range(6):
                    word_index = self.word_2_index[word]
                    word_embedding = torch.tensor(self.w1[word_index][None][None])
                    pre, (h_0, c_0) = model(word_embedding, h_0, c_0)
                    word = self.index_2_word[int(torch.argmax(pre))]
                    result += word
                result += punctuation_list[i]
            print(result)

params = {
        "batch_size": 32,  # batch大小
        "epochs": 1000,  # epoch大小
        "lr": 0.003,  # 学习率
        "hidden_num": 64,  # 隐层大小
        "embedding_num": 128,  # 词向量维度
        "train_num": 1000,  # 训练的故事数量, 七言古诗:0~6290, 五言古诗:0~2929
        "optimizer": torch.optim.AdamW,  # 优化器 , 注意不要加括号
        "batch_num_test": 100,  # 多少个batch 打印一首古诗进行效果测试
    }

model = Poetry_Model_lstm(params)  # 模型定义
model = model.to_train()  # 模型训练

model.generate_poetry_acrostic()

请输入四个汉字:鸡你太美

输出:鸡头跣卉总渊润,嗜蟹镇山休垣而。太嫌闽晏竖泛爱,美席驯帆竖绣操

Q2

from keras.layers import Dense,Embedding,Activation
from keras.layers.embeddings import Embedding
from keras.layers.recurrent import LSTM
from keras.models import Sequential
from sklearn.model_selection import train_test_split
from keras.preprocessing import sequence
import collections
import numpy as np

## 统计train.txt中最长句子的长度maxlen;句子条数num_recs;每词的词词频word_freqs[]
maxlen = 0
word_freqs = collections.Counter()   #创建一个类,跟踪值出现的次数
num_recs = 0

import nltk
with open('train.txt','r',encoding='utf-8') as f:
    for line in f:
        label, sentence = line.strip().split("\t")
        words = nltk.word_tokenize(sentence.lower())  ## 分词 >>> import nltk  >>> nltk.download('punkt')
        if len(words) > maxlen:  # 找最长句子
            maxlen = len(words)  
        for word in words:    #统计词频
            word_freqs[word] += 1   # token: freq
        num_recs += 1   # 样本个数

print('max_len ',maxlen)   #最长句子
print('nb_words ', len(word_freqs))   # 单词个数

# 总词汇表长为2002:2000单词+ 超出语料的新词UNK+ 短句填充词0
# 生成单词表和数字表
MAX_FEATURES = 2000    # 词典大小
MAX_SENTENCE_LENGTH = 40  # 句子长度40
# word2index, index2word, 单词和下标转换表
vocab_size = min(MAX_FEATURES, len(word_freqs)) + 2     #词表长度2002,其中包括两个PAD, UNK

word2index = {x[0]: i+2 for i, x in enumerate(word_freqs.most_common(MAX_FEATURES))}  # token: index(2~ )
word2index["PAD"] = 0   # 下标为0
word2index["UNK"] = 1   # 下标为1
index2word = {v:k for k, v in word2index.items()}    # index2word, word2index 

# data preparation,生成句子表和序号表
X = np.empty(num_recs,dtype=list)  # 7086个样本
y = np.zeros(num_recs)
i=0
with open('train.txt','r+',encoding='utf-8') as f:
    for line in f:
        label, sentence = line.strip().split("\t")
        words = nltk.word_tokenize(sentence.lower())
        seqs = []
        for word in words:
            if word in word2index:
                seqs.append(word2index[word])   # 句子中的词下标加入 seqs
            else:
                seqs.append(word2index["UNK"])  #词典中没有的词
        X[i] = seqs    # X[i]存的句子中单词的下标
        y[i] = int(label)   # 7086个标记
        i += 1
X = sequence.pad_sequences(X, maxlen=MAX_SENTENCE_LENGTH)   # X为下标矩阵 7086 * 40

### 实验要求:

1. 按照训练集,测试集8:2的比例划分数据集

2. 为模型添加embedding layer、lstm layer

这个是选做,我好像没做

Logo

北京人形旗下天工造物具身智能开源社区,聚焦具身天工与慧思开物两大平台

更多推荐