【NLP 16、实践 ③ 找出特定字符在字符串中的位置】

看着父亲苍老的白发和渐渐老态的面容

希望时间再慢一些

                                                —— 24.12.19

一、定义模型

1.初始化模型

① 初始化父类

super(TorchModel, self).__init__(): 调用父类 nn.Module 初始化方法,确保模型能够正确初始化。

② 创建嵌入层

self.embedding = nn.Embedding(len(vocab), vector_dim): 创建一个嵌入层,将词汇表中的每个词映射到一个 vector_dim 维度的向量。

③ 创建RNN层

self.rnn = nn.RNN(vector_dim, vector_dim, batch_first=True): 创建一个 RNN 层输入和输出的特征维度均为 vector_dim,并且输入数据的第一维是批量大小。

④ 创建线性分类层

self.classify = nn.Linear(vector_dim, sentence_length + 1): 创建一个线性层,将 RNN 输出的特征向量映射到 sentence_length + 1 个分类标签。+1 是因为可能有某个词不存在的情况,此时的真实标签被设为 sentence_length。

⑤ 定义损失函数

self.loss = nn.functional.cross_entropy: 定义交叉熵损失函数,用于计算模型预测值与真实标签之间的差异。

class TorchModel(nn.Module):
    def __init__(self, vector_dim, sentence_length, vocab):
        super(TorchModel, self).__init__()
        self.embedding = nn.Embedding(len(vocab), vector_dim)  #embedding层
        # self.pool = nn.AvgPool1d(sentence_length)   #池化层
        #可以自行尝试切换使用rnn
        self.rnn = nn.RNN(vector_dim, vector_dim, batch_first=True)

        # +1的原因是可能出现a不存在的情况,那时的真实label在构造数据时设为了sentence_length
        self.classify = nn.Linear(vector_dim, sentence_length + 1)
        self.loss = nn.functional.cross_entropy

2、前向传播定义

① 输入嵌入

x = self.embedding(x):将输入 x 通过嵌入层转换为向量表示

② RNN处理

rnn_out, hidden = self.rnn(x):将嵌入后的向量输入到RNN层,得到每个时间步的输出 rnn_out 和最后一个时间步的隐藏状态 hidden。

③ 提取特征

x = rnn_out[:, -1, :]:从RNN的输出中提取最后一个时间步(最后一维)的特征向量。

④ 分类

y_pred = self.classify(x):将提取的特征向量通过线性层进行分类,得到预测值 y_pred。

⑤ 损失计算

如果提供了真实标签 y,则计算并返回损失值;否则,返回预测值。

    #当输入真实标签,返回loss值;无真实标签,返回预测值
    def forward(self, x, y=None):
        x = self.embedding(x)
        #使用pooling的情况,先使用pooling池化层会丢失模型语句的时序信息
        # x = x.transpose(1, 2)
        # x = self.pool(x)
        # x = x.squeeze()
        #使用rnn的情况
        # rnn_out:每个字对应的向量  hidden:最后一个输出的隐含层对应的向量
        rnn_out, hidden = self.rnn(x)
        # 中间维度改变,变成(batch_size数据样本数量, sentence_length文本长度, vector_dim向量维度)
        x = rnn_out[:, -1, :]  #或者写hidden.squeeze()也是可以的,因为rnn的hidden就是最后一个位置的输出

        #接线性层做分类
        y_pred = self.classify(x)
        if y is not None:
            return self.loss(y_pred, y)   #预测值和真实值计算损失
        else:
            return y_pred     

二、数据

1.建立词表

① 定义字符集

定义一个字符集 chars,包含字母 'a' 到 'k'。

② 定义字典

初始化一个字典 vocab,其中键为 'pad',值为 0。

③ 遍历字符集

使用 enumerate 遍历字符集 chars,为每个字符分配一个唯一的序号,从 1 开始。

④ 定义unk键

添加一个特殊的键 'unk',其值为当前字典的长度(即 26)。

⑤ 返回词汇表

将生成的词汇表返回

#字符集随便挑了一些字,实际上还可以扩充
#为每个字生成一个标号
#{"a":1, "b":2, "c":3...}
#abc -> [1,2,3]
def build_vocab():
    chars = "abcdefghijk"  #字符集
    vocab = {"pad":0}
    for index, char in enumerate(chars):
        vocab[char] = index+1   #每个字对应一个序号
    vocab['unk'] = len(vocab) #26
    return vocab

2.随机生成样本

① 采样

random.sample(list(vocab.keys()), sentence_length):从词汇表 vocab 的键中随机选择 sentence_length 个不同的字符,生成列表 x

② 标签生成

index('a'):检查列表 x 中是否包含字符 "a",如果包含,记录 "a" 在列表中的索引位置为 y,否则,设置 y 为 sentence_length。

③ 转换

将列表 x 中的每个字符转换为其在词汇表中的序号,如果字符不在词汇表中,则使用 unk 的序号

④ 返回结果

返回转换后的列表 x 和标签 y

#随机生成一个样本
def build_sample(vocab, sentence_length):
    #注意这里用sample,是不放回的采样,每个字母不会重复出现,但是要求字符串长度要小于词表长度
    x = random.sample(list(vocab.keys()), sentence_length)
    #指定哪些字出现时为正样本
    if "a" in x:
        y = x.index("a")
    else:
        y = sentence_length
    x = [vocab.get(word, vocab['unk']) for word in x]   #将字转换成序号,为了做embedding
    return x, y

3.建立数据集

① 初始化数据集

创建两个空列表 dataset_x 和 dataset_y,用于存储生成的样本和对应的标签

② 生成样本

使用 for 循环,循环次数为 sample_length,即需要生成的样本数量。在每次循环中,调用 build_sample 函数生成一个样本 (x, y),其中 x 是输入数据,y 是标签

③ 存储样本

将生成的样本 x 添加到 dataset_x 列表中。将生成的标签 y 添加到 dataset_y 列表

④ 返回数据集

将 dataset_x 和 dataset_y 转换为 torch.LongTensor 类型,以便在 PyTorch 中使用。返回转换后的数据集。

#建立数据集
#输入需要的样本数量。需要多少生成多少
def build_dataset(sample_length, vocab, sentence_length):
    dataset_x = []
    dataset_y = []
    for i in range(sample_length):
        x, y = build_sample(vocab, sentence_length)
        dataset_x.append(x)
        dataset_y.append(y)
    return torch.LongTensor(dataset_x), torch.LongTensor(dataset_y)

三、模型测试、训练、评估

1.建立模型

① 参数:

vocab:词汇表,通常是一个包含所有字符或单词的列表或字典

char_dim:字符的维度,即每个字符在嵌入层中的向量长度

sentence_length:句子的最大长度

② 过程:

使用传入的参数 char_dim、sentence_length 和 vocab 实例化一个 TorchModel 对象并返回

#建立模型
def build_model(vocab, char_dim, sentence_length):
    model = TorchModel(char_dim, sentence_length, vocab)
    return model

2.测试模型

① 设置模型为评估模式

model.eval():将模型设置为评估模式禁用 dropout 训练时的行为

② 生成测试数据集

调用 build_dataset 函数生成 200 个用于测试的样本

③ 打印样本数量

输出当前测试集中样本的数量

④ 模型预测

使用 torch.no_grad() 禁用梯度计算,提高推理速度并减少内存消耗,然后对生成的测试数据进行预测

⑤ 计算准确率

遍历预测结果和真实标签,统计正确和错误的预测数量,并计算准确率

⑥ 输出结果

打印正确预测的数量和准确率,并返回准确率

#测试代码
#用来测试每轮模型的准确率
def evaluate(model, vocab, sample_length):
    model.eval()
    x, y = build_dataset(200, vocab, sample_length)   #建立200个用于测试的样本
    print("本次预测集中共有%d个样本"%(len(y)))
    correct, wrong = 0, 0
    with torch.no_grad():
        y_pred = model(x)      #模型预测
        for y_p, y_t in zip(y_pred, y):  #与真实标签进行对比
            if int(torch.argmax(y_p)) == int(y_t):
                correct += 1
            else:
                wrong += 1
    print("正确预测个数:%d, 正确率:%f"%(correct, correct/(correct+wrong)))
    return correct/(correct+wrong)

3.模型训练

① 配置参数

设置训练轮数epoch_num批量大小batch_size训练样本数train_sample字符维度char_dim句子长度sentence_length学习率learning_rate

② 建立字表

调用 build_vocab 函数生成字符到索引的映射。

③ 建立模型

调用 build_model 函数创建模型。

④ 选择优化器

torch.optim.Adam(model.parameters(), lr=learning_rate):使用 Adam 优化器

⑤ 训练过程

model.train():模型进入训练模式。每个 epoch 中,按批量生成训练数据,计算损失,反向传播并更新权重。记录每个 epoch 的平均损失

⑥ 评估模型

每个 epoch 结束后,调用 evaluate 函数评估模型性能。

⑦ 记录日志

记录每个 epoch 的准确率和平均损失

⑧ 绘制图表

绘制准确率和损失的变化曲线

⑨ 保存模型和词表

保存模型参数和词表

def main():
    #配置参数
    epoch_num = 20        #训练轮数
    batch_size = 40       #每次训练样本个数
    train_sample = 1000    #每轮训练总共训练的样本总数
    char_dim = 30         #每个字的维度
    sentence_length = 10   #样本文本长度
    learning_rate = 0.001 #学习率
    # 建立字表
    vocab = build_vocab()
    # 建立模型
    model = build_model(vocab, char_dim, sentence_length)
    # 选择优化器
    optim = torch.optim.Adam(model.parameters(), lr=learning_rate)
    log = []
    # 训练过程
    for epoch in range(epoch_num):
        model.train()
        watch_loss = []
        for batch in range(int(train_sample / batch_size)):
            x, y = build_dataset(batch_size, vocab, sentence_length) #构造一组训练样本
            optim.zero_grad()    #梯度归零
            loss = model(x, y)   #计算loss
            loss.backward()      #计算梯度
            optim.step()         #更新权重
            watch_loss.append(loss.item())
        print("=========\n第%d轮平均loss:%f" % (epoch + 1, np.mean(watch_loss)))
        acc = evaluate(model, vocab, sentence_length)   #测试本轮模型结果
        log.append([acc, np.mean(watch_loss)])
    #画图
    plt.plot(range(len(log)), [l[0] for l in log], label="acc")  #画acc曲线
    plt.plot(range(len(log)), [l[1] for l in log], label="loss")  #画loss曲线
    plt.legend()
    plt.show()
    #保存模型
    torch.save(model.state_dict(), "model.pth")
    # 保存词表
    writer = open("vocab.json", "w", encoding="utf8")
    writer.write(json.dumps(vocab, ensure_ascii=False, indent=2))
    writer.close()
    return

四、模型预测

1.训练并保存模型

if __name__ == "__main__":
    main()

 


2.预测数据

用保存的训练好的模型进行预测

① 初始化参数

设置每个字的维度 char_dim 和样本文本长度 sentence_length

② 加载字符表

从指定路径加载字符表 vocab

③ 建立模型

调用 build_model 函数构建模型

④ 加载模型权重

从指定路径加载预训练的模型权重

⑤ 序列化输入

将输入字符串转换为模型所需的输入格式

⑥ 模型预测

将输入数据传递给模型进行预测

⑦ 输出结果

打印每个输入字符串的预测类别和概率值

#使用训练好的模型做预测
def predict(model_path, vocab_path, input_strings):
    char_dim = 30  # 每个字的维度
    sentence_length = 10  # 样本文本长度
    vocab = json.load(open(vocab_path, "r", encoding="utf8")) #加载字符表
    model = build_model(vocab, char_dim, sentence_length)     #建立模型
    model.load_state_dict(torch.load(model_path,weights_only=True))             #加载训练好的权重
    x = []
    for input_string in input_strings:
        x.append([vocab[char] for char in input_string])  #将输入序列化
    model.eval()   #测试模式
    with torch.no_grad():  #不计算梯度
        result = model.forward(torch.LongTensor(x))  #模型预测
    for i, input_string in enumerate(input_strings):
        print("输入:%s, 预测类别:%s, 概率值:%s" % (input_string, torch.argmax(result[i]), result[i])) #打印结果

3.调用函数进行预测

if __name__ == "__main__":
    # main()
    test_strings = ["kijabcdefh", "gijkbcdeaf", "gkijadfbec", "kijhdefacb"]
    predict("model.pth", "vocab.json", test_strings)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/939443.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

lightRAG 论文阅读笔记

论文原文 https://arxiv.org/pdf/2410.05779v1 这里我先说一下自己的感受,这篇论文整体看下来,没有太多惊艳的地方。核心就是利用知识图谱,通过模型对文档抽取实体和关系。 然后基于此来构建查询。核心问题还是在解决知识之间的连接问题。 论…

基于JAVA+SpringBoot+Vue的反欺诈平台

基于JAVASpringBootVue的反欺诈平台 前言 ✌全网粉丝20W,csdn特邀作者、博客专家、CSDN[新星计划]导师、java领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ 🍅文末附源码下载链接🍅 哈喽兄弟…

Gartner发布2025年网络安全主要趋势:实现转型和嵌入弹性两大主题下的9个趋势

持续不断的技术和业务中断考验着安全计划和团队绩效的极限。安全和风险管理领导者必须实现业务价值,并加倍努力增强组织、个人和团队的韧性,以证明安全计划在 2025 年的有效性。 机会 面对不断变化的技术以及企业希望利用这些技术获得战略利益的愿望&…

基于Spring Boot的新能源汽车个性化推荐系统

一、系统背景与意义 随着新能源汽车市场的快速发展,消费者对新能源汽车的需求日益多样化。为了满足消费者的个性化需求,提高购车体验,开发一个基于Spring Boot的新能源汽车个性化推荐系统显得尤为重要。该系统能够根据用户的偏好、历史行为等…

YOLOV8 原理和实现全解析(合适新人)

YOLOV8 原理和实现全解析 0 简介1 YOLOv8 概述2 模型结构设计3 Loss 计算4 训练数据增强5 训练策略6 模型推理过程7 特征图可视化总结 0 简介 图 1:YOLOv8-P5 模型结构 以上结构图由 RangeKinggithub 绘制。 YOLOv8 是 Ultralytics 公司在 2023 年 1月 10 号开源的…

【WRF教程第四期】WRF 初始化概述:以4.5版本为例

WRF 初始化(WRF Initialization) Building Initialization Programs编译方式 理想案例初始化(Initialization for Idealized Cases)理想化案例的输入可用的理想化案例 现实案例初始化(Initialization for Real Data Ca…

vmcore-dmesg交叉编译(arm64平台)

kexec工具:能够将第二内核(捕获内核)装载到指定内存运行。 vmcore-dmesg工具:用于提取vmcore的dmesg信息。 1、源码下载地址: Index of /pub/linux/utils/kernel/kexec/ 2、交叉编译: 采用aarch64-lin…

计算机网络-L2TP VPN基础概念与原理

一、概述 前面学习了GRE和IPSec VPN,今天继续学习另外一个也很常见的VPN类型-L2TP VPN。 L2TP(Layer 2 Tunneling Protocol) 协议结合了L2F协议和PPTP协议的优点,是IETF有关二层隧道协议的工业标准。L2TP是虚拟私有拨号网VPDN&…

OpenCV学习——图像融合

import cv2 as cv import cv2 as cvbg cv.imread("test_images/background.jpg", cv.IMREAD_COLOR) fg cv.imread("test_images/forground.png", cv.IMREAD_COLOR)# 打印图片尺寸 print(bg.shape) print(fg.shape)resize_size (1200, 800)bg cv.resize…

ChatGPT重大更新:新增实时搜索和高级语音

12月17日消息,据报道,OpenAI开启了第八天技术分享直播,对ChatGPT搜索功能进行了大量更新。 此次ChatGPT新增的功能亮点纷呈。其中,实时搜索功能尤为引人注目。OpenAI对搜索算法进行了深度优化,使得用户提出问题后&…

30. Three.js案例-绘制并渲染圆弧

30. Three.js案例-绘制并渲染圆弧 实现效果 知识点 WebGLRenderer WebGLRenderer 是 Three.js 中用于渲染 3D 场景的核心类。它利用 WebGL 技术在浏览器中渲染 3D 图形。 构造器 new THREE.WebGLRenderer(parameters) 参数类型描述parametersObject可选参数对象&#xff…

YOLO8 改进 009:引入 ASFF 对 YOLOv8 检测头进行优化(适用于小目标检测任务)

论文题目:Learning Spatial Fusion for Single-Shot Object Detection 论文地址:Paper - ASFF 官方源码:GitHub - GOATmessi8/ASFF 简 介 多尺度特征融合是解决多尺度目标检测问题的关键技术,其中 FPN(特征金字塔网络…

【数据集】生菜病害检测数据集530张6类YOLO+VOC格式

数据集格式:VOC格式YOLO格式 压缩包内含:3个文件夹,分别存储图片、xml、txt文件 JPEGImages文件夹中jpg图片总计:530 Annotations文件夹中xml文件总计:530 labels文件夹中txt文件总计:530 标签种类数&#…

设计模式2

23中设计模式分类 创建型模式:对象实例化的模式,创建型模式用于解耦对象的实例化过程。(对象的创建和对象的使用分离) 5种:单例模式、工厂模式、抽象工厂模式、原型模式、建造者模式 结构型模式:把类或对…

CSS边框的样式

边框阴影 让元素更有立体感 img {box-shadow: 2px 10px 5px 20px #ff0000;border-radius: 44px;}语法:box-shadow:值1 值2 值3 值4 值5 值1:水平阴影的位置值2:垂直阴影的位置值3:模糊距离值4:阴影的尺寸…

Spring篇--xml方式整合第三方框架

Spring xml方式整合第三方框架 xml整合第三方框架有两种整合方案: ​ 不需要自定义名空间,不需要使用Spring的配置文件配置第三方框架本身内容,例如:MyBatis; ​ 需要引入第三方框架命名空间,需要使用…

Javascript-web API-day02

文章目录 01-事件监听02-点击关闭广告03-随机点名案例04-鼠标经过或离开事件05-可点击的轮播图06-小米搜索框07-键盘类型事件08-键盘事件-发布评论案例09-focus选择器10-评论回车发布11-事件对象12-trim方法13-环境对象14-回调函数15-tab栏切换 01-事件监听 <!DOCTYPE html…

powershell(1)

免责声明 学习视频来自 B 站up主泷羽sec&#xff0c;如涉及侵权马上删除文章。 笔记的只是方便各位师傅学习知识&#xff0c;以下代码、网站只涉及学习内容&#xff0c;其他的都与本人无关&#xff0c;切莫逾越法律红线&#xff0c;否则后果自负。 泷羽sec官网&#xff1a;http…

GraphReader: 将长文本结构化为图,并让 agent 自主探索,结合的大模型长文本处理增强方法

GraphReader: 将长文本结构化为图&#xff0c;并让 agent 自主探索&#xff0c;结合的大模型长文本处理增强方法 论文大纲理解为什么大模型和知识图谱不够&#xff1f;还要多智能体 设计思路数据分析解法拆解全流程核心模式提问为什么传统的长文本处理方法会随着文本长度增加而…

如何一站式计算抗体和蛋白信息

在生物医药研究领域&#xff0c;蛋白质&#xff08;抗体、多肽等&#xff09;的性质计算是理解生命机制、分离/纯化/鉴定/生产蛋白、以及开发蛋白新药的重要研究手段。然而&#xff0c;很多相关功能分散在不同的软件中&#xff0c;十分不方便。鹰谷电子实验记录本InELN一站式内…