C#构建一个简单的循环神经网络,模拟对话

循环神经网络(Recurrent Neural Network, RNN)是一种用于处理序列数据的神经网络模型。与传统的前馈神经网络不同,RNN具有内部记忆能力,可以捕捉到序列中元素之间的依赖关系。这种特性使得RNN在自然语言处理、语音识别、时间序列预测等需要考虑上下文信息的任务中表现出色。

RNN的基本结构

RNN的基本结构包括输入层、隐藏层和输出层。在处理序列数据时,RNN会按照序列的时间顺序逐个处理每个元素。对于序列中的每一个时间步,RNN不仅会接收该时间步的输入,还会接收上一个时间步的隐藏状态作为输入。这样,通过将之前的信息传递给后续的处理步骤,RNN能够利用历史信息来影响当前的输出。

方法

  • InitializeWeightsAndBiases():使用随机值初始化权重矩阵和偏置向量。
  • Sigmoid():激活函数,用于隐藏层的非线性变换。
  • RandomMatrix():生成指定大小的随机矩阵,用于权重的初始化。
  • Softmax():通常用于多分类问题中的输出层,将输出转换为概率分布。
  • Forward():前向传播方法,根据输入数据计算每个时间步的输出。它会更新隐藏状态,并最终返回所有时间步的输出列表。
  • Backward():反向传播方法,用于根据预测输出与目标输出之间的差异调整模型参数。它计算梯度并更新权重和偏置。
  • UpdateWeights():根据计算出的梯度更新模型的权重和偏置。
  • Train():训练模型的方法,通过多次迭代(epoch)对输入数据进行前向传播和反向传播,以优化模型参数。
  • Predict():预测方法,根据输入数据返回每个时间步的预测结果索引,即输出概率最高的类别。

说明

这只是一个基础的 RNN 模型实现,实际应用中可能需要考虑更多的优化技术,比如使用长短期记忆网络(LSTM)、门控循环单元(GRU)等更复杂的架构来改善性能。

using System;
using System.Linq;
using System.Collections.Generic;namespace Project.NeuralNetwork
{/// <summary>/// 构建神经网络/// </summary>public class RnnModel{/// <summary>/// 输入层大小/// </summary>private readonly int _inputSize;/// <summary>/// 隐藏层大小/// </summary>private readonly int _hiddenSize;/// <summary>/// 输出层大小/// </summary>private readonly int _outputSize;/// <summary>/// 输入到隐藏层的权重/// </summary>private double[,] _weightsInputHidden;/// <summary>/// 隐藏层到隐藏层的权重/// </summary>private double[,] _weightsHiddenHidden;/// <summary>/// 隐藏层到输出层的权重/// </summary>private double[,] _weightsHiddenOutput;/// <summary>/// 隐藏层偏置/// </summary>private double[] _biasHidden;/// <summary>/// 输出层偏置/// </summary>private double[] _biasOutput;/// <summary>/// 隐藏层状态/// </summary>private double[] _hiddenState;/// <summary>/// 初始化模型的构造函数/// </summary>/// <param name="inputSize"></param>/// <param name="hiddenSize"></param>/// <param name="outputSize"></param>public RnnModel(int inputSize, int hiddenSize, int outputSize){_inputSize = inputSize;_hiddenSize = hiddenSize;_outputSize = outputSize;InitializeWeightsAndBiases();}/// <summary>/// 初始化权重和偏置/// </summary>private void InitializeWeightsAndBiases(){_weightsInputHidden = RandomMatrix(_inputSize, _hiddenSize);_weightsHiddenHidden = RandomMatrix(_hiddenSize, _hiddenSize);_weightsHiddenOutput = RandomMatrix(_hiddenSize, _outputSize);_biasHidden = new double[_hiddenSize];_biasOutput = new double[_outputSize];}/// <summary>/// 激活函数/// </summary>/// <param name="x"></param>/// <returns></returns>private double Sigmoid(double x){return 1 / (1 + Math.Exp(-x));}/// <summary>/// 生成随机矩阵/// </summary>/// <param name="rows"></param>/// <param name="cols"></param>/// <returns></returns>private double[,] RandomMatrix(int rows, int cols){var matrix = new double[rows, cols];var random = new Random();for (int i = 0; i < rows; i++){for (int j = 0; j < cols; j++){matrix[i, j] = random.NextDouble() * 2 - 1; // [-1, 1]}}return matrix;}/// <summary>/// 前向传播/// </summary>/// <param name="inputs"></param>/// <returns></returns>public List<double[]> Forward(List<double[]> inputs){_hiddenState = new double[_hiddenSize];var outputs = new List<double[]>();foreach (var input in inputs){var hidden = new double[_hiddenSize];for (int h = 0; h < _hiddenSize; h++){hidden[h] = _biasHidden[h];for (int i = 0; i < _inputSize; i++){hidden[h] += _weightsInputHidden[i, h] * input[i];}for (int hh = 0; hh < _hiddenSize; hh++){hidden[h] += _weightsHiddenHidden[hh, h] * _hiddenState[hh];}hidden[h] = Sigmoid(hidden[h]);}_hiddenState = hidden;var output = Output(hidden);outputs.Add(output);}return outputs;}/// <summary>/// 输出层/// </summary>/// <param name="h"></param>/// <returns></returns>private double[] Output(double[] h){double[] y = new double[_outputSize];for (int i = 0; i < _outputSize; i++){double sum = _biasOutput[i];for (int j = 0; j < _hiddenSize; j++){sum += h[j] * _weightsHiddenOutput[j, i];}y[i] = sum;}return Softmax(y);}/// <summary>/// 输出层的激活函数/// </summary>/// <param name="x"></param>/// <returns></returns>private double[] Softmax(double[] x){double max = x.Max();double expSum = x.Select(xi => Math.Exp(xi - max)).Sum();return x.Select(xi => Math.Exp(xi - max) / expSum).ToArray();}/// <summary>/// 反向传播/// </summary>/// <param name="inputs"></param>/// <param name="targets"></param>/// <param name="outputs"></param>/// <param name="learningRate"></param>private void Backward(List<double[]> inputs, List<double[]> targets, List<double[]> outputs, double learningRate){//输入到隐藏层的梯度double[,] dWeightsInputHidden = new double[_inputSize, _hiddenSize];//隐藏层到隐藏层的梯度double[,] dWeightsHiddenHidden = new double[_hiddenSize, _hiddenSize];//隐藏层到输出层的梯度double[,] dWeightsHiddenOutput = new double[_hiddenSize, _outputSize];//隐藏层的偏置double[] dBiasHidden = new double[_hiddenSize];//输出层的偏置double[] dBiasOutput = new double[_outputSize];for (int t = inputs.Count - 1; t >= 0; t--){double[] targetVector = new double[_outputSize];Array.Copy(targets[t], targetVector, _outputSize);// 计算输出层的误差for (int o = 0; o < _outputSize; o++){dBiasOutput[o] = outputs[t][o] - targetVector[o];}// 计算隐藏层到输出层的梯度for (int o = 0; o < _outputSize; o++){for (int h = 0; h < _hiddenSize; h++){dWeightsHiddenOutput[h, o] += dBiasOutput[o] * _hiddenState[h];}}// 计算隐藏层的偏置double[] dh = new double[_hiddenSize];for (int h = 0; h < _hiddenSize; h++){double error = 0;for (int o = 0; o < _outputSize; o++){error += dBiasOutput[o] * _weightsHiddenOutput[h, o];}dh[h] = error * (_hiddenState[h] * (1 - _hiddenState[h]));}for (int h = 0; h < _hiddenSize; h++){dBiasHidden[h] += dh[h];}//计算输入到隐藏层的梯度for (int h = 0; h < _hiddenSize; h++){for (int i = 0; i < _inputSize; i++){dWeightsInputHidden[i, h] += dh[h] * inputs[t][i];}}// 计算输入到隐藏层的梯度if (t > 0){for (int h = 0; h < _hiddenSize; h++){for (int hh = 0; hh < _hiddenSize; hh++){dWeightsHiddenHidden[hh, h] += dh[h] * _hiddenState[hh];}}}}// 更新权重和偏置UpdateWeights(dWeightsInputHidden, dWeightsHiddenHidden, dWeightsHiddenOutput, dBiasHidden, dBiasOutput, learningRate);}/// <summary>/// 更新权重/// </summary>/// <param name="dWxh"></param>/// <param name="dWhh"></param>/// <param name="dWhy"></param>/// <param name="dbh"></param>/// <param name="dby"></param>/// <param name="learningRate"></param>private void UpdateWeights(double[,] dWeightsInputHidden, double[,] dWeightsHiddenHidden, double[,] dWeightsHiddenOutput, double[] dBiasHidden, double[] dBiasOutput, double learningRate){// 更新输入到隐藏层的权重for (int i = 0; i < _inputSize; i++){for (int h = 0; h < _hiddenSize; h++){_weightsInputHidden[i, h] -= learningRate * dWeightsInputHidden[i, h];}}//更新隐藏层到隐藏层的权重for (int h = 0; h < _hiddenSize; h++){for (int hh = 0; hh < _hiddenSize; hh++){_weightsHiddenHidden[h, hh] -= learningRate * dWeightsHiddenHidden[h, hh];}}//更新隐藏层到输出层的权重for (int h = 0; h < _hiddenSize; h++){for (int o = 0; o < _outputSize; o++){_weightsHiddenOutput[h, o] -= learningRate * dWeightsHiddenOutput[h, o];}}//更新隐藏层的偏置for (int h = 0; h < _hiddenSize; h++){_biasHidden[h] -= learningRate * dBiasHidden[h];}//更新输出层的偏置for (int o = 0; o < _outputSize; o++){_biasOutput[o] -= learningRate * dBiasOutput[o];}}/// <summary>/// 训练/// </summary>/// <param name="inputs"></param>/// <param name="targets"></param>/// <param name="epochs"></param>/// <param name="learningRate"></param>public void Train(List<List<double[]>> inputs, List<List<double[]>> targets, double learningRate, int epochs){for (int epoch = 0; epoch < epochs; epoch++){for (int i = 0; i < inputs.Count; i++){List<double[]> input = inputs[i];List<double[]> target = targets[i];List<double[]> outputs = Forward(input);Backward(input, target, outputs, learningRate);}}}/// <summary>/// 预测/// </summary>/// <param name="inputs"></param>/// <returns></returns>public int[] Predict(List<double[]> inputs){var output = Forward(inputs);var predictedIndices = output.Select(o => Array.IndexOf(o, o.Max())).ToArray();return predictedIndices;}}
}
  • 准备训练数据
  • 训练网络
  • 测试并输出结果
public static void Rnn_Predict()
{// 定义数据集var data = new List<Tuple<string[], string[]>>{Tuple.Create(new string[] { "早安" }, new string[] { "早上好" }),Tuple.Create(new string[] { "午安" }, new string[] { "中午好" }),Tuple.Create(new string[] { "晚安" }, new string[] { "晚上好" }),Tuple.Create(new string[] { "你好吗?" }, new string[] { "我很好,谢谢。" })};// 创建词汇表var allWords = data.SelectMany(t => t.Item1.Concat(t.Item2)).Distinct().ToList();var wordToIndex = allWords.ToDictionary(word => word, word => allWords.IndexOf(word));// 将字符串转换为one-hot编码List<List<double[]>> inputsData = new List<List<double[]>>();List<List<double[]>> targetsData = new List<List<double[]>>();foreach (var item in data){var inputSequence = item.Item1.Select(word => OneHotEncode(word, wordToIndex)).ToList();var targetSequence = item.Item2.Select(word => OneHotEncode(word, wordToIndex)).ToList();inputsData.Add(inputSequence);targetsData.Add(targetSequence);}double[] OneHotEncode(string word, Dictionary<string, int> wordToIndex){var encoding = new double[wordToIndex.Count];encoding[wordToIndex[word]] = 1;return encoding;}//开始训练int inputSize = allWords.Count;int hiddenSize = allWords.Count;int outputSize = allWords.Count;RnnModel model = new RnnModel(inputSize, hiddenSize, outputSize);int epochs = 10000;double learningRate = 0.1;model.Train(inputsData, targetsData, learningRate, epochs);//预测while (true){Console.Write("你: ");string userInput = Console.ReadLine();if (userInput.ToLower() == "exit"){break;}if (!allWords.Contains(userInput)){Console.WriteLine("对不起,我不认识这些词。");continue;}var testInput = new List<double[]> { OneHotEncode(userInput, wordToIndex) };var prediction = model.Predict(testInput);var predictedWords = prediction.Select(index => allWords[index]).ToArray();Console.WriteLine($"机器人: {string.Join(", ", predictedWords)}");}
}

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

单片机_简单AI模型训练与部署__从0到0.9

IDE&#xff1a; CLion MCU&#xff1a; STM32F407VET6 一、导向 以求知为导向&#xff0c;从问题到寻求问题解决的方法&#xff0c;以兴趣驱动学习。 虽从0&#xff0c;但不到1&#xff0c;剩下的那一小步将由你迈出。本篇主要目的是体验完整的一次简单AI模型部署流程&#x…

Python3 爬虫 Scrapy的安装

Scrapy是基于Python的分布式爬虫框架。使用它可以非常方便地实现分布式爬虫。Scrapy高度灵活&#xff0c;能够实现功能的自由拓展&#xff0c;让爬虫可以应对各种网站情况。同时&#xff0c;Scrapy封装了爬虫的很多实现细节&#xff0c;所以可以让开发者把更多的精力放在数据的…

golang实现TCP服务器与客户端的断线自动重连功能

1.服务端 2.客户端 生成服务端口程序: 生成客户端程序: 测试断线重连: 初始连接成功

【Spring Boot】# 使用@Scheduled注解无法执行定时任务

1. 前言 在 Spring Boot中&#xff0c;使用Scheduled注解来定义定时任务时&#xff0c;定时任务不执行&#xff1b;或未在规定时间执行。 import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Component;Component public c…

java 老矣,尚能饭否?

随笔 从千万粉丝“何同学”抄袭开源项目说起&#xff0c;为何纯技术死路一条&#xff1f; 数据源的统一与拆分 监控报警系统的指标、规则与执行闭环 java 老矣&#xff0c;尚能饭否&#xff1f; 一骑红尘妃子笑&#xff0c;无人知是荔枝来! java 老吗&#xff1f; 去年看…

[译]Elasticsearch Sequence ID实现思路及用途

原文地址:https://www.elastic.co/blog/elasticsearch-sequence-ids-6-0 如果 几年前&#xff0c;在Elastic&#xff0c;我们问自己一个"如果"问题&#xff0c;我们知道这将带来有趣的见解&#xff1a; "如果我们在Elasticsearch中对索引操作进行全面排序会怎样…

解锁PPTist的全新体验:Windows系统环境下本地部署与远程访问

文章目录 前言1. 本地安装PPTist2. PPTist 使用介绍3. 安装Cpolar内网穿透4. 配置公网地址5. 配置固定公网地址 前言 在Windows系统环境中&#xff0c;如何本地部署开源在线演示文稿应用PPTist&#xff0c;并实现远程访问&#xff1f;本文将为您提供详细的部署和配置指南。 P…

一文学会Golang里拼接字符串的6种方式(性能对比)

g o l a n g golang golang的 s t r i n g string string类型是不可修改的&#xff0c;对于拼接字符串来说&#xff0c;本质上还是创建一个新的对象将数据放进去。主要有以下几种拼接方式 拼接方式介绍 1.使用 s t r i n g string string自带的运算符 ans ans s2. 使用…

IEC61850读服务器目录命令——GetServerDirectory介绍

IEC61850标准中的GetServerDirectory命令是变电站自动化系统中非常重要的一个功能&#xff0c;它主要用于读取服务器的目录信息&#xff0c;特别是服务器的逻辑设备节点&#xff08;LDevice&#xff09;信息。以下是对GetServerDirectory命令的详细介绍。 目录 一、命令功能 …

Flink学习连载第二篇-使用flink编写WordCount(多种情况演示)

使用Flink编写代码&#xff0c;步骤非常固定&#xff0c;大概分为以下几步&#xff0c;只要牢牢抓住步骤&#xff0c;基本轻松拿下&#xff1a; 1. env-准备环境 2. source-加载数据 3. transformation-数据处理转换 4. sink-数据输出 5. execute-执行 DataStream API开发 //n…

数据集-目标检测系列- 花卉 玫瑰 检测数据集 rose >> DataBall

数据集-目标检测系列- 花卉 玫瑰 检测数据集 rose >> DataBall DataBall 助力快速掌握数据集的信息和使用方式&#xff0c;会员享有 百种数据集&#xff0c;持续增加中。 贵在坚持&#xff01; 数据样例项目地址&#xff1a; * 相关项目 1&#xff09;数据集可视化项…

Windows系统运行库软件游戏修复工具

本页面下载的资源包包括PC电脑常用的运行库和电脑必备组件&#xff0c;如您的电脑出现应用打不开&#xff0c;缺少dll链接库、闪退等现象可以尝试用下面软件修复。 本资源永久有效。 软件安装基本常识科普&#xff1a; 为什么要安装运行库&#xff1f;运行库默认安装到C盘&…

wireshark使用lua解析自定义协议

wireshark解析自定义协议 1.自定义的lua放入路径2.修改init.lua2.1 开启lua2.2 init.lua文件最后加入自己的lua文件位置&#xff0c;这里需要确保与自己的文件名相同 3.编写lua4.编写c抓包5.wireshark添加自定义协议如何加调试信息 1.自定义的lua放入路径 一般是自己软件的安装…

ISAAC Gym 7. 使用箭头进行数据可视化

在这里发布一个ISAAC GYM可以使用的箭头绘制类。 gymutil默认有WireframeBoxGeometry&#xff0c;WireframeBBoxGeometry&#xff0c; WireframeSphereGeometry三个线段集生成函数&#xff0c;可以绘制盒子和球体。绘制函数分别有draw_lines和draw_line。 同理&#xff0c;使…

【计算机网络】网段划分

一、为什么有网段划分 IP地址 网络号(目标网络) 主机号(目标主机) 网络号: 保证相互连接的两个网段具有不同的标识 主机号: 同一网段内&#xff0c;主机之间具有相同的网络号&#xff0c;但是必须有不同的主机号 互联网中的每一台主机&#xff0c;都要隶属于某一个子网 -&…

机器学习周志华学习笔记-第5章<神经网络>

机器学习周志华学习笔记-第5章<神经网络> 卷王&#xff0c;请看目录 5模型的评估与选择5.1 神经元模型5.2 感知机与多层网络5.3 BP(误逆差)神经网络算法 5.4常见的神经网络5.4.1 RBF网络&#xff08;Radial Basis Function Network&#xff0c;径向基函数网络&#xff0…

MySQL数据库设计

数据库设计 数据库是用来存在数据的&#xff0c;需要设计合理的数据表来存放数据–能够完成数据的存储&#xff0c;同时能够方便的提取应该系统所需的数据 1. 数据库的设计流程 数据库是为应用系统服务的&#xff0c;数据库的数据存储也是由应用系统决定的 当我们进行应用系统开…

Spring Boot 3.x + OAuth 2.0:构建认证授权服务与资源服务器

Spring Boot 3.x OAuth 2.0&#xff1a;构建认证授权服务与资源服务器 前言 随着Spring Boot 3的发布&#xff0c;我们迎来了许多新特性和改进&#xff0c;其中包括对Spring Security和OAuth 2.0的更好支持。本文将详细介绍如何在Spring Boot 3.x版本中集成OAuth 2.0&#xf…

数据可视化复习2-绘制折线图+条形图(叠加条形图,并列条形图,水平条形图)+ 饼状图 + 直方图

目录 目录 一、绘制折线图 1.使用pyplot 2.使用numpy ​编辑 3.使用DataFrame ​编辑 二、绘制条形图&#xff08;柱状图&#xff09; 1.简单条形图 2.绘制叠加条形图 3.绘制并列条形图 4.水平条形图 ​编辑 三、绘制饼状图 四、绘制散点图和直方图 1.散点图 2…

logback 初探学习

logback 三大模块 记录器&#xff08;Logger&#xff09;、追加器&#xff08;Appender&#xff09;和布局&#xff08;Layout&#xff09; 配置文件外层最基本的标签如图示 xml中定义的就是这个三个东西下面进入学习 包引入参考springboot 官方文档 Logging :: Spring Boo…