【深度学习】手动实现RNN循环神经网络

news2024/12/25 9:32:30

🌻个人主页:相洋同学
🥇学习在于行动、总结和坚持,共勉!

目录

01 回顾

02 RNN神经网络原理

 03 RNN神经网络实现

04 RNN神经网络实验


RNN的特别结构使得RNN具备了短期记忆能力,使其能够学习部分语义信息。

01 回顾

       RNN的特别结构使得RNN具备了短期记忆能力,使其能够学习部分语义信息。

       我们回顾一下网络结构的全连接层,又称线性层,计算公式:y=w*x + b

       w和b是参与训练的参数,w的维度决定了隐含层输出的维度

       参考我之前的文章:【深度学习】手动实现全连接神经网络(FCNN)-CSDN博客

02 RNN神经网络原理

       时序相关问题:要处理的任务是有一个序列的,后一步可能受到前一步的影响,NLP问题是天然的时序问题

       RNN主要思想:将整个序列划分成多个时间步,将每一个时间步的信息依次输入模型,同时将模型输出的结果传给下一个时间步

从数学上实现方式:

       我们首先来观察一下样本,假设我们每个样本是一句话,一句话由若干字组成

       如果使用传统池化方法,对相应维度做加和求平均。我们就丧失了语义信息:I am a cat 和 cat am a I似乎就没有了区别 

       RNN的优势这个时候就凸显了出来,RNN就是将样本进行了多个时间步的处理,后一个时间步等于前一个时间步信息做线性变换,参数为W,再加上当前U与xt的乘积,这样我们既存储了当前时间步信息,也继承了前面时间步的信息。

 03 RNN神经网络实现

       我们首先用pytorch来实现RNN网络的,而后通过自己构造的RNN,对我们构造的RNN网络进行验证

# 使用torch创建RNN层,不进行训练,记录初始化的参数w_ih,w_hh
import torch
import torch.nn as nn
import numpy as np

class TorchRNN(nn.Module):
    def __init__(self,input_size,hidden_size):
        super(TorchRNN,self).__init__()
        self.layer = nn.RNN(input_size,hidden_size,bias=False,batch_first=True)

    def forward(self,x):
        return self.layer(x)
        
x = np.array([[1, 2, 3, 4],
              [3, 4, 5, 6],
              [5, 6, 7, 8],
              [7, 8, 9, 10]])  #网络输入

#torch实验
hidden_size = 5
torch_model = TorchRNN(4, hidden_size)

# print(torch_model.state_dict())
w_ih = torch_model.state_dict()["layer.weight_ih_l0"] # w_ih就是U
w_hh = torch_model.state_dict()["layer.weight_hh_l0"] # w_hh就是W

       我们自己再构造一个RNN

class MyRNN:
    def __init__(self,w_ih,w_hh,hidden_size):
        self.w_ih = w_ih
        self.w_hh = w_hh
        self.hidden_size = hidden_size

    def forward(self,x):
        ht = np.zeros((self.hidden_size))
        output = []
        for xt in x:
            ux = np.dot(xt,self.w_ih.T)  # xt维度:1*4,w_ih.T维度:4*5,ux维度:4*5
            wh = np.dot(ht,self.w_hh.T)  # ht维度:1*5,w_hh.T维度:5*5
            ht_next = np.tanh(ux+wh)  # 维度:4*5
            output.append(ht_next)
            ht = ht_next
        return np.array(output),ht

x = np.array([[1, 2, 3, 4],
              [3, 4, 5, 6],
              [5, 6, 7, 8],
              [7, 8, 9, 10]])  #网络输入

       最后预测输出,对比结果:

torch_x = torch.FloatTensor([x])
output, h = torch_model.forward(torch_x)
print(h)
print(output.detach().numpy(), "torch模型预测结果")
print(h.detach().numpy(), "torch模型预测隐含层结果")
print("---------------")
diy_model = MyRNN(w_ih, w_hh, hidden_size)
output, h = diy_model.forward(x)
print(output, "diy模型预测结果")
print(h, "diy模型预测隐含层结果")

==========================
[[[ 0.9770124  -0.98210144 -0.898459    0.43363687 -0.7096077 ]
  [ 0.99852514 -0.9999082  -0.97600037  0.87200433 -0.49491423]
  [ 0.999937   -0.9999985  -0.9864487   0.9611373  -0.592286  ]
  [ 0.99999815 -1.         -0.99189234  0.98961574 -0.72276914]]] torch模型预测结果
[[[ 0.99999815 -1.         -0.99189234  0.98961574 -0.72276914]]] torch模型预测隐含层结果
---------------
[[ 0.97701239 -0.98210147 -0.89845902  0.43363689 -0.70960771]
 [ 0.99852516 -0.9999082  -0.97600034  0.87200431 -0.49491426]
 [ 0.99993697 -0.9999985  -0.98644868  0.96113729 -0.59228603]
 [ 0.99999817 -0.99999997 -0.99189236  0.98961571 -0.72276908]] diy模型预测结果
[ 0.99999817 -0.99999997 -0.99189236  0.98961571 -0.72276908] diy模型预测隐含层结果

       我们可以看到相差不大,第一个矩阵中是所有时间步的结果,最后隐含层的结果就是最后一个时间步的结果

04 RNN神经网络实验

       我们再构造一个任务:预测字母a出现的位置

import torch
import torch.nn as nn
import numpy as np
import random
import json
import matplotlib.pyplot as plt

"""
基于pytorth的网络编写
实现一个网络完成一个简单nlp任务
判断文本中是否有某些特定字符出现
"""

class TorchModel(nn.Module):
    def __init__(self, vector_dim, sentence_length, vocab,hidden_dim,output_dim):
        super(TorchModel, self).__init__()
        self.embedding = nn.Embedding(len(vocab), vector_dim)  #embedding层

        self.rnn = nn.RNN(vector_dim, hidden_dim,batch_first=True)     #线性层    hidden_dim是隐藏层维度,最好与vector_dim相同或者是它的倍数
        self.classify = nn.Linear(hidden_dim, output_dim) # 线性层,将RNN的输出维度转化为最终输出维度
        self.loss = nn.CrossEntropyLoss()  #loss函数采用均方差损失

    # 当输入真实标签,返回loss值,无真实标签,返回预测值
    #当输入真实标签,返回loss值;无真实标签,返回预测值
    def forward(self, x, y=None):

        x = self.embedding(x)                      #(batch_size, sen_len) -> (batch_size, sen_len, vector_dim)
        x,_ = self.rnn(x)           # (batch_size, sen_len, vector_dim) -> (batch_size, sen_len, hidden_dim)
        # 取rnn最后一个时间步的输出
        x = x[:, -1, :]
        y_pred = self.classify(x)                       #(batch_size, vector_dim) -> (batch_size, 1) 3*5 5*1 -> 3*1
             #(batch_size, 1) -> (batch_size, 1)
        if y is not None:
            return self.loss(y_pred, y)   #预测值和真实值计算损失
        else:
            return y_pred                 #输出预测结果

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

def build_sample(vocab, sentence_length):
    # 随机选择除'a'外的其他字符,总数为sentence_length-1
    remaining_keys = list(vocab.keys())
    remaining_keys.remove('a')  # 移除'a',避免重复选择
    x = random.sample(remaining_keys, sentence_length - 1)
    x.append('a')  # 确保'a'被包含在样本中
    random.shuffle(x)  # 打乱列表,使'a'的位置随机

    # 计算'a'的位置
    y = x.index('a')

    # 将字符转换为索引
    x = [vocab[word] for word in x]

    return x, y

# 建立数据集
# 建立需要的样本数量。需要多少生成多少
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)

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

# 测试代码
def evaluate(model, vocab, sentence_length):
    model.eval()  # 将模型设置为评估模式
    test_sample_num = 200
    x, y = build_dataset(test_sample_num, vocab, sentence_length)
    correct = 0  # 记录正确预测的数量
    with torch.no_grad():  # 不计算梯度
        y_pred = model(x)  # 模型预测
        _, predicted_labels = torch.max(y_pred, 1)  # 获取最大概率的索引,即预测的类别
        correct += (predicted_labels == y).sum().item()  # 计算正确预测的数量
    print(f'本次测试集预测准确率为{correct / test_sample_num}')
    return correct / test_sample_num

def main():
    epoch_num = 20   # 训练轮数
    batch_size = 2    # 每轮训练样本数
    train_sample = 50   # 每轮训练的样本总数
    char_dim = 20     # 每个字的维度
    sentence_length = 5    # 样本文本长度
    learning_rate = 0.01    # 学习率
    hidden_dim = 10
    output_dim = 5

    vocab = build_vocab()

    # 建立模型
    model = build_model(char_dim, sentence_length, vocab,hidden_dim,output_dim)

    # 选择优化器
    optimizer = torch.optim.Adam(model.parameters(),lr=learning_rate)
    log = []
    x,y = build_dataset(train_sample,vocab,sentence_length)

    # 训练过程
    for epoch in range(epoch_num):
        model.train()
        watch_loss = []
        for batch_index in range(train_sample//batch_size):
            x_train = x[batch_index*batch_size:(batch_index+1)*batch_size]
            y_train = y[batch_index*batch_size:(batch_index+1)*batch_size]

            optimizer.zero_grad()
            loss = model(x_train,y_train)
            loss.backward()
            optimizer.step()

            watch_loss.append(loss.item())

        print(f'===========第{epoch+1}轮训练结果,平均loss:{np.mean(watch_loss)}============')
        acc = evaluate(model,vocab,sentence_length)
        log.append([acc,float(np.mean(watch_loss))])
    # 保存模型
    torch.save(model.state_dict(),'nlpmodel.pth')
    # 保存词表
    writer = open("vocab.json", "w", encoding="utf8")
    writer.write(json.dumps(vocab, ensure_ascii=False, indent=2))
    writer.close()

    # 画图:
    plt.plot(range(1,epoch_num+1),[i[0] for i in log],label='acc')
    plt.plot(range(1,epoch_num+1),[i[1] for i in log],label='loss')
    plt.legend()
    plt.show()
    return

def predicr(model_path,vocab_path,input_strings):  # 加载模型
    char_dim = 20
    sentence_length = 5
    hidden_dim = 10
    output_dim = 5
    vocab = json.load(open(vocab_path,'r',encoding='utf-8'))  # 加载字符表
    model = build_model(char_dim, sentence_length, vocab,hidden_dim,output_dim)
    model.load_state_dict(torch.load(model_path))

    x = []
    for i in input_strings:
        x.append([vocab[j] for j in i])  # 将输入文本序列化
    model.eval()  # 测试模式
    with torch.no_grad():
        result = model(torch.LongTensor(x))
        _, predicted_positions = torch.max(result, dim=1)  # 获取每个样本最大概率的位置
        for i, pred_pos in enumerate(predicted_positions):
            print(f'输入:{input_strings[i]},预测位置:{pred_pos.item()}')  # 打印每个输入字符串的预测位置

if __name__ == '__main__':
    main()
    test_strings = ["abcde", "bacdf", "aebdc",]
    predicr("nlpmodel.pth", "vocab.json", test_strings)

        可视化

       仅仅20轮,模型准确率就达到了1

 

以上

互联网是最好的课本,实践是最好的老师,AI是最好的学习助手

行动起来,共勉

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

php 对接IronSource海外广告平台收益接口Reporting API

今天对接的是IronSource广告reporting api接口,拉取广告收益回来自己做统计。记录分享给大家 首先是文档地址,进入到IronSource后台就能看到文档地址以及参数: 文档地址:https://developers.is.com/ironsource-mobile/air/reporting/ 在这里插…

用户中心项目(前后端环境搭建)

文章目录 1.企业做项目流程2.需求分析1.登录/注册2.用户管理(仅管理员可见)3.用户校验 3.技术选型4.计划5.初始化项目1.前端初始化1.ant design pro 官网2.ant design pro 初始化1.本地创建一个文件夹存放项目2.cmd进入该目录3.根据官网进行初始化项目4.…

《C语言深度剖析》---------关键字(1)

1.双击实质--->加载内存 windows系统里面,双击的本质就是运行程序,把程序加载到内存里面; 任何程序运行的时候都必须加载到内存里面; 程序没有运行之前在硬盘里面,为什么程序运行之前必须加载到内存里面呢&#…

免费的chatgpt网站(包含最新版4.0)

相信每个人在生活工作学习中都逃不过用chatgpt来解决一些问题,下面我长话短说,为大家简单介绍几款免费且好用的chatgpt网站 1、YesChat 网址:YesChat-ChatGPT4V Dalle3 Claude 3 All in One Free 第一个就给大家介绍一个狠角色,最…

万界星空科技铜杆加工行业生产管理MES系统

传统的铜管加工方法有: (1)铜管挤压加工技术(2)铜管上引连铸法(3)铜管(有缝)焊接生产技术(4)铸轧法生产精密铜管铸轧法 生产精密铜管是一种全新的生产工艺,…

154.乐理基础-和弦固定标记法(三)九音、十一音、十三音

如果到这五线谱还没记住还不认识的话去看102.五线谱-高音谱号与103.五线谱-低音谱号这两个里,这里面有五线谱对应的音名,对比着看 内容参考于:三分钟音乐社 上一个内容:153.和弦的织体 上一个内容里练习的答案: 接下…

开放签开源电子签章白皮书-简版

开放签开源电子签章白皮书-简版 一、摘要: 开放签电子签章团队源自于电子合同SaaS公司,立志于通过开源、开放的模式,结合团队十多年的行业经验,将电子签章产品更简单、更低门槛的推广到各行各业中。让电子签章应用更简单&#x…

31.HarmonyOS App(JAVA)鸿蒙系统app Service服务的用法

鸿蒙系统app Service服务的用法 后台任务调度和管控 HarmonyOS将应用的资源使用生命周期划分为前台、后台和挂起三个阶段。前台运行不受资源调度的约束,后台会根据应用业务的具体任务情况进行资源使用管理,在挂起状态时,会对应用的资源使用进…

2024年熔化焊接与热切割证模拟考试题库及熔化焊接与热切割理论考试试题

题库来源:安全生产模拟考试一点通公众号小程序 2024年熔化焊接与热切割证模拟考试题库及熔化焊接与热切割理论考试试题是由安全生产模拟考试一点通提供,熔化焊接与热切割证模拟考试题库是根据熔化焊接与热切割最新版教材,熔化焊接与热切割大…

n皇后问题(典dfs )注意对角线状态判断

思路&#xff1a;用的dfs思想&#xff0c;第一种是全排列思路&#xff0c;和数字排列同样的步骤。要注意对对角线的判断。下面画了个图简单示意一下&#xff0c;但是 u 和 i 的位置变了&#xff0c;在代码里呈现不一样。明天再改吧。先睡了。 代码&#xff1a; #include<io…

IDE(集成开发环境)插件是安全开发的便捷方式之一

开发人员每天都使用插件&#xff0c;插件的功能在于简化开发流程&#xff0c;例如自动检测所有特殊字符&#xff08;如“;”、“:”&#xff09;或语法合规性。创建插件的目的本身就是为了让开发人员能够在编写代码时检测漏洞&#xff0c;并在无需离开 IDE 环境的情况下立即修复…

STM32CubeMX学习笔记25---FreeRTOS信号量

一、信号量简介 信号量用于同步&#xff0c;任务间或者任务和中断间同步 互斥量用户互锁&#xff0c;用于保护同时只能有一个任务访问的资源&#xff0c;为资源上一把锁。 二值信号量&#xff1a;同步。如果存在两个线程&#xff0c;为线程1和线程2&#xff0c;如果线程1发送了…

大载重无人机基础技术,研发一款50KG负重六旋翼无人机技术及成本分析

六旋翼无人机是一种多旋翼无人机&#xff0c;具有六个旋翼&#xff0c;通常呈“X”形布局。它采用电动串列式结构&#xff0c;具有垂直起降、悬停、前飞、后飞、侧飞、俯仰、翻滚等多种飞行动作的能力。六旋翼无人机通常被用于航拍、农业植保、环境监测、地形测绘等领域。 六旋…

PolarDN MISC(简单)大礼包 :详细思路过程

0和255 题目给了俩个文件&#xff0c;一个.txt,一个.py .txt文件中包含0和255 一个字节有八位&#xff0c;每一位只能储存1或0&#xff0c;计算机只懂二进制&#xff0c;所以就是2的八次方&#xff0c;又计算机规定从0开始计数&#xff0c;所以是0至255 考虑用编码转换工具将其…

Android: Gradle 命令

一、查看整个项目依赖传递关系 x.x.x (*) 该依赖已经有了&#xff0c;将不再重复依赖。x.x.x -> x.x.x 该依赖的版本被箭头所指的版本代替。x.x.x -> x.x.x(*) 该依赖的版本被箭头所指的版本代替&#xff0c;并且该依赖已经有了&#xff0c;不再重复依赖。 1. gradlew ap…

redis常用五大数据类型

目录 Key 字符串String 常用命令 列表List 常用命令 集合Set 常用命令 Hash哈希 键值对集合 有序集合Zset Redis新数据类型 Key set key value...添加keykeys *查看当前库中所有的keyexist key该key是否存在type keykey的类型del key删除keyunlink key根据value选择非阻塞…

基于springboot+vue的火锅店管理系统

博主主页&#xff1a;猫头鹰源码 博主简介&#xff1a;Java领域优质创作者、CSDN博客专家、阿里云专家博主、公司架构师、全网粉丝5万、专注Java技术领域和毕业设计项目实战&#xff0c;欢迎高校老师\讲师\同行交流合作 ​主要内容&#xff1a;毕业设计(Javaweb项目|小程序|Pyt…

2024.3.16-408学习笔记-C-数据在内存中的存储

1、整数型存储 整数型存储就是所有整型家族里的数据类型的存储方式&#xff0c;也就是说包含了字符类型的存储&#xff08;因为字符的操作符的返回值是ASCII码值&#xff0c;故实际上存储的是整数&#xff09;。 1.1、有符号整数 有符号整数包含char&#xff0c;short&#x…

Combining external-latent attention for medical image segmentation

结合外部潜在注意的医学图像分割 摘要 注意机制是提高医学图像分割性能的新切入点。如何合理分配权重是注意力机制的关键,目前流行的方法包括全局压缩和使用自注意操作的非局部信息交互。然而,这些方法过于关注外部特征,缺乏对潜在特征的开发。全局压缩方法通过全局均值或…

(一)基于IDEA的JAVA基础3

通过之前的内容&#xff0c;我们在建好的文件夹下建一个java文件&#xff0c;我们来在IDEA中写一下之前用记事本写的helloworld&#xff0c;我们先看一下java代码的规范: 1.java程序文件名一定要有意义&#xff0c;首字母一定要大写。 2.class后面的名字:由大小写字母&#x…