前言

有好几个月没搞神经网络代码了,期间也就是回顾了两边之前的文字。

不料,对nn,cnn的理解反而更深入了-_-!。

修改

《零基础学习人工智能—Python—Pytorch学习(四)》,关于Linear的理解是错误的,已修改。

RNN

RNN是Recurrent Neural Network的缩写。中文叫循环神经网络。

RNN是在NN的基础上的扩展。

我们知道在训练NN的时候,要传递一个待训练的tensor。RNN简单来说就是在传递tensor的同时,再传递一个hidden。

这个hidden是哪来的?它是上一轮训练时,返回y_predict的同时,返回的(我们之前写NN只会返回y_predict,现在要多返回一个hidden)。

如果没有上一轮,那hidden就等于空的tensor。

代码实现

定义RNN

class RNN(nn.Module):
# implement RNN from scratch rather than using nn.RNN
def __init__(self, input_size, hidden_size, output_size):
super(RNN, self).__init__() self.hidden_size = hidden_size
self.i2h = nn.Linear(input_size + hidden_size, hidden_size)
self.i2o = nn.Linear(input_size + hidden_size, output_size)
self.softmax = nn.LogSoftmax(dim=1) def forward(self, input_tensor, hidden_tensor):
combined = torch.cat((input_tensor, hidden_tensor), 1) hidden = self.i2h(combined) #input to hidden=i2h
output = self.i2o(combined) #input to output=i2o
output = self.softmax(output)
return output, hidden def init_hidden(self):
return torch.zeros(1, self.hidden_size)

代码分析

init

init函数增加了一个参数hidden_size。我们知道init函数是接受x的列和y_predict的列的,x的列是input,y_predict的列是输出。

现在我们增加了hidden_size,他是hidden层的tensor的列。

我们在使用linear的时候,会把input_size和hidden_size相加,因为,我们后面再前向传播的时候,也会把x的tensor和hidden的tensor组合成一个向量。

forward

forward函数,我们先看return。

return多返回了一个对象——hidden,hidden是一个tensor,是执行linear(i2h)时的output,他也是根据x进行线性变换后的tensor,因为不是最后输出的tensor,所以他叫做hidden,而最后输出的output的tensor叫做y_predict。

我们在看forward的第一行,combined = torch.cat((input_tensor, hidden_tensor), 1)。

这是把tensor变量input_tensor和hidden_tensor给合并成了一个tensor,使用的是torch.cat函数。参数1是指按列的方向合并。

即input_tensor如果是(3,5),hidden_tensor是(3,2),我们将得到一个(3,7),相当与把hidden的每一行的值取出来,添加进相同行的input_tensor里。

所以我们上面的init里,初始化linear的第一个参数(x的列)是input_size + hidden_size。

定义工具函数

import io
import os
import unicodedata
import string
import glob import torch
import random # alphabet small + capital letters + " .,;'"
ALL_LETTERS = string.ascii_letters + " .,;'"
N_LETTERS = len(ALL_LETTERS) # Turn a Unicode string to plain ASCII, thanks to https://stackoverflow.com/a/518232/2809427
def unicode_to_ascii(s):
return ''.join(
c for c in unicodedata.normalize('NFD', s)
if unicodedata.category(c) != 'Mn'
and c in ALL_LETTERS
) def load_data():
# Build the category_lines dictionary, a list of names per language
category_lines = {}
all_categories = [] def find_files(path):
return glob.glob(path) # Read a file and split into lines
def read_lines(filename):
lines = io.open(filename, encoding='utf-8').read().strip().split('\n')
return [unicode_to_ascii(line) for line in lines] for filename in find_files('data/names/*.txt'):
category = os.path.splitext(os.path.basename(filename))[0]
all_categories.append(category) lines = read_lines(filename)
category_lines[category] = lines return category_lines, all_categories """
To represent a single letter, we use a “one-hot vector” of
size <1 x n_letters>. A one-hot vector is filled with 0s
except for a 1 at index of the current letter, e.g. "b" = <0 1 0 0 0 ...>. To make a word we join a bunch of those into a
2D matrix <line_length x 1 x n_letters>. That extra 1 dimension is because PyTorch assumes
everything is in batches - we’re just using a batch size of 1 here.
""" # Find letter index from all_letters, e.g. "a" = 0
def letter_to_index(letter):
return ALL_LETTERS.find(letter) # Just for demonstration, turn a letter into a <1 x n_letters> Tensor
def letter_to_tensor(letter):
tensor = torch.zeros(1, N_LETTERS)
tensor[0][letter_to_index(letter)] = 1
return tensor # Turn a line into a <line_length x 1 x n_letters>,
# or an array of one-hot letter vectors
def line_to_tensor(line):
tensor = torch.zeros(len(line), 1, N_LETTERS)
for i, letter in enumerate(line):
tensor[i][0][letter_to_index(letter)] = 1
return tensor def random_training_example(category_lines, all_categories): def random_choice(a):
random_idx = random.randint(0, len(a) - 1)
return a[random_idx] category = random_choice(all_categories)
line = random_choice(category_lines[category])
category_tensor = torch.tensor([all_categories.index(category)], dtype=torch.long)
line_tensor = line_to_tensor(line)
return category, line, category_tensor, line_tensor if __name__ == '__main__':
print(ALL_LETTERS)
print(unicode_to_ascii('Ślusàrski')) category_lines, all_categories = load_data()
print(category_lines['Italian'][:5]) print(letter_to_tensor('J')) # [1, 57]
print(line_to_tensor('Jones').size()) # [5, 1, 57]

代码分析

string.ascii_letters 是一个字符串常量,它包含了所有 ASCII 字母,即小写字母 'abcdefghijklmnopqrstuvwxyz' 和大写字母 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 的组合

letter_to_tensor函数:是把入参的字母转成tensor,tensor的shape是(1,57),后面我们使用该向量做神经网络的输入。

one-hot 向量

letter_to_tensor函数输出是tensor被称为one-hot向量,直译就是一个热点向量,即向量里就一个值是有意义的值。

【one-hot向量】就是一行N列的向量,列的值为1时,该列的位置,就是字母在字符串的位置。

如果我们有以下字母表,共27个字符(26个字母 + 空格):

ALL_LETTERS = "abcdefghijklmnopqrstuvwxyz "

转成one-hot 向量,就会如下图模式分配。

load_data函数:查询一个文件夹下的全部文件,然后把文件名全部取出来,组成集合,然后把文件的内容取出来,结合文件的名字,组成字典。

glob.glob:系统函数,查询指定目录下的指定文件。

data.names文件夹如下:

RNN前向传播

category_lines, all_categories = load_data()
n_categories = len(all_categories) n_hidden = 128
print("N_LETTERS",N_LETTERS)
rnn = RNN(N_LETTERS, n_hidden, n_categories) # RNN(57,128,18) 18是18个国家即18个类别 input_tensor = letter_to_tensor('A') # 把A转成tensor(1,57)
hidden_tensor = rnn.init_hidden() output, next_hidden = rnn(input_tensor, hidden_tensor) input_tensor = line_to_tensor('Albert')
hidden_tensor = rnn.init_hidden() output, next_hidden = rnn(input_tensor[0], hidden_tensor) def category_from_output(output):
category_idx = torch.argmax(output).item()
return all_categories[category_idx] print(category_from_output(output))

代码分析

torch.argmax(output).item()是取出tensor里的最大值,其最大值的索引。

这里,我们通过整个RNN的前向传播,得到了一个output,即通过线性转换,我们得到y_predict,然后我们找到y_predict里最大的值的索引,我们认为,我们这个索引对应国家集合里对应索引的国家名,就是我们预测的我们输入的名字(实际输入的是字母)的所属国家。

完整代码:
import threading

import torch
import torch.nn as nn
from utils import ALL_LETTERS, N_LETTERS
from utils import load_data, letter_to_tensor, line_to_tensor, random_training_example def category_from_output(output):
category_idx = torch.argmax(output).item()
return all_categories[category_idx]
class RNN(nn.Module):
# implement RNN from scratch rather than using nn.RNN
def __init__(self, input_size, hidden_size, output_size):
super(RNN, self).__init__() self.hidden_size = hidden_size
self.i2h = nn.Linear(input_size + hidden_size, hidden_size)
self.i2o = nn.Linear(input_size + hidden_size, output_size)
self.softmax = nn.LogSoftmax(dim=1) def forward(self, input_tensor, hidden_tensor):
combined = torch.cat((input_tensor, hidden_tensor), 1) hidden = self.i2h(combined) #input to hidden=i2h
output = self.i2o(combined) #input to output=i2o
output = self.softmax(output)
return output, hidden def init_hidden(self):
return torch.zeros(1, self.hidden_size) category_lines, all_categories = load_data()
n_categories = len(all_categories) n_hidden = 128
print("N_LETTERS",N_LETTERS)
rnn = RNN(N_LETTERS, n_hidden, n_categories) # RNN(57,128,18) 18是18个国家即18个类别 input_tensor = letter_to_tensor('A') # 把A转成tensor(1,57)
hidden_tensor = rnn.init_hidden() output, next_hidden = rnn(input_tensor, hidden_tensor) print(category_from_output(output)) input_tensor = line_to_tensor('Albert')
hidden_tensor = rnn.init_hidden() output, next_hidden = rnn(input_tensor[0], hidden_tensor) print(category_from_output(output))

使用RNN训练及预测

定义训练函数

def train(line_tensor, category_tensor):
hidden = rnn.init_hidden() for i in range(line_tensor.size()[0]):
output, hidden = rnn(line_tensor[i], hidden) loss = criterion(output, category_tensor) optimizer.zero_grad()
loss.backward()
optimizer.step() return output, loss.item()

代码分析

入参line_tensor是一个tesnor,shape是(n,1,57),即,是一个字符串转成n个one-hot向量。他是一个名字的字符串。

入参category_tensor是真实的y值,这里他的shape是tensor(1),即,这个名字的真实国家的索引。

然后我们通过循环执行output, hidden = rnn(line_tensor[i], hidden),把每个字母都在rnn里进行线性变换,这里每次都会把hidden返回,然后下次调用rnn,会把hideen传递过去,形成循环网络。

最后输出的output是个1行18列的tensor,1行是因为我们输出的x是1行,18列是因为我们创建rnn时,指定了18列的输出。

然后我们执行损失函数,loss = criterion(output, category_tensor),计算损失值,损失函数有很多种。(我们可以简单这样理解损失函数的运行逻辑,上面通过线性计算把X转成了tensor(1,18),这个tensor是y_predict,现在我们计算y_predicd与真y的差,在把结果平方,在求平均数,反正就是计算出一个数,这个数如果是2,3,那就代表我们的模型错的很离谱)

损失值求完,我们就可以使用损失值进行逆向传播了loss.backward()。这里loss是触发逆向传播的,也就是计算梯度,也就是求偏导数,之所以是loss触发,是因为loss本身是通过函数计算出来的,而我们求梯度,是对这个求loss的函数偏导,但是loss值,并不参与求偏导,所以loss是触发逆向传播的启点。

完整代码
import threading

import torch
import torch.nn as nn
import matplotlib.pyplot as plt from utils import ALL_LETTERS, N_LETTERS
from utils import load_data, letter_to_tensor, line_to_tensor, random_training_example def category_from_output(output):
category_idx = torch.argmax(output).item()
return all_categories[category_idx]
class RNN(nn.Module):
# implement RNN from scratch rather than using nn.RNN
def __init__(self, input_size, hidden_size, output_size):
super(RNN, self).__init__() self.hidden_size = hidden_size
self.i2h = nn.Linear(input_size + hidden_size, hidden_size)
self.i2o = nn.Linear(input_size + hidden_size, output_size)
self.softmax = nn.LogSoftmax(dim=1) def forward(self, input_tensor, hidden_tensor):
combined = torch.cat((input_tensor, hidden_tensor), 1) hidden = self.i2h(combined) #input to hidden=i2h
output = self.i2o(combined) #input to output=i2o
output = self.softmax(output)
return output, hidden def init_hidden(self):
return torch.zeros(1, self.hidden_size) category_lines, all_categories = load_data()
# print("category_lines",category_lines)
# print("all_categories",all_categories)
n_categories = len(all_categories) n_hidden = 128
rnn = RNN(N_LETTERS, n_hidden, n_categories) criterion = nn.NLLLoss()
learning_rate = 0.005
optimizer = torch.optim.SGD(rnn.parameters(), lr=learning_rate) def train(line_tensor, category_tensor):
hidden = rnn.init_hidden() for i in range(line_tensor.size()[0]):
output, hidden = rnn(line_tensor[i], hidden) loss = criterion(output, category_tensor) optimizer.zero_grad()
loss.backward()
optimizer.step() return output, loss.item() current_loss = 0
all_losses = []
plot_steps, print_steps = 1000, 5000
n_iters = 100000
for i in range(n_iters):
category, line, category_tensor, line_tensor = random_training_example(category_lines, all_categories) # category=国家名 line=这个国家下的随机一个名字 category_tensor=国家名索引值转换成的tensor(1) line_tensor=名字转换成的tensor(n,1,57) output, loss = train(line_tensor, category_tensor)
current_loss += loss if (i+1) % plot_steps == 0:
all_losses.append(current_loss / plot_steps)
current_loss = 0 if (i+1) % print_steps == 0:
guess = category_from_output(output)
correct = "CORRECT" if guess == category else f"WRONG ({category})"
print(f"{i+1} {(i+1)/n_iters*100} {loss:.4f} {line} / {guess} {correct}") def show():
plt.figure()
plt.plot(all_losses)
plt.show()
show() def predict(input_line):
print(f"\n> {input_line}")
with torch.no_grad():
line_tensor = line_to_tensor(input_line) hidden = rnn.init_hidden() for i in range(line_tensor.size()[0]):
output, hidden = rnn(line_tensor[i], hidden) guess = category_from_output(output)
print("guess:",guess) while True:
sentence = input("Input:")
if sentence == "quit":
break predict(sentence)

到此,Python—Pytorch学习-RNN(一)就介绍完了。


参考网站:https://github.com/patrickloeber/pytorch-examples/tree/master/rnn-name-classification


注:此文章为原创,任何形式的转载都请联系作者获得授权并注明出处!



若您觉得这篇文章还不错,请点击下方的【推荐】,非常感谢!

https://www.cnblogs.com/kiba/p/18844861

Python—Pytorch学习-RNN(一)的更多相关文章

  1. 7大python 深度学习框架的描述及优缺点绍

    Theano https://github.com/Theano/Theano 描述: Theano 是一个python库, 允许你定义, 优化并且有效地评估涉及到多维数组的数学表达式. 它与GPUs ...

  2. 新手必备 | 史上最全的PyTorch学习资源汇总

    目录: PyTorch学习教程.手册 PyTorch视频教程 PyTorch项目资源      - NLP&PyTorch实战      - CV&PyTorch实战 PyTorch论 ...

  3. Day1 Python基础学习

    一.编程语言分类 1.简介 机器语言:站在计算机的角度,说计算机能听懂的语言,那就是直接用二进制编程,直接操作硬件 汇编语言:站在计算机的角度,简写的英文标识符取代二进制去编写程序,本质仍然是直接操作 ...

  4. Day1 Python基础学习——概述、基本数据类型、流程控制

    一.Python基础学习 一.编程语言分类 1.简介 机器语言:站在计算机的角度,说计算机能听懂的语言,那就是直接用二进制编程,直接操作硬件 汇编语言:站在计算机的角度,简写的英文标识符取代二进制去编 ...

  5. 【深度学习】Pytorch学习基础

    目录 pytorch学习 numpy & Torch Variable 激励函数 回归 区分类型 快速搭建法 模型的保存与提取 批训练 加速神经网络训练 Optimizer优化器 CNN MN ...

  6. Pytorch学习记录-torchtext和Pytorch的实例( 使用神经网络训练Seq2Seq代码)

    Pytorch学习记录-torchtext和Pytorch的实例1 0. PyTorch Seq2Seq项目介绍 1. 使用神经网络训练Seq2Seq 1.1 简介,对论文中公式的解读 1.2 数据预 ...

  7. Pytorch学习--编程实战:猫和狗二分类

    Pytorch学习系列(一)至(四)均摘自<深度学习框架PyTorch入门与实践>陈云 目录: 1.程序的主要功能 2.文件组织架构 3. 关于`__init__.py` 4.数据处理 5 ...

  8. Python基础学习之环境搭建

    Python如今成为零基础编程爱好者的首选学习语言,这和Python语言自身的强大功能和简单易学是分不开的.今天我们将带领Python零基础的初学者完成入门的第一步——环境搭建.本文会先来区分几个在P ...

  9. [深度学习] Pytorch学习(一)—— torch tensor

    [深度学习] Pytorch学习(一)-- torch tensor 学习笔记 . 记录 分享 . 学习的代码环境:python3.6 torch1.3 vscode+jupyter扩展 #%% im ...

  10. 【原】Learning Spark (Python版) 学习笔记(三)----工作原理、调优与Spark SQL

    周末的任务是更新Learning Spark系列第三篇,以为自己写不完了,但为了改正拖延症,还是得完成给自己定的任务啊 = =.这三章主要讲Spark的运行过程(本地+集群),性能调优以及Spark ...

随机推荐

  1. Java中的Scanner、BufferedReader 和 StreamTokenizer

    1. Scanner 的使用与分析 简介: Scanner 是 Java 中一个用于解析原始类型(如 int.double 等)和字符串的类.它通常从输入流中逐个读取数据并进行解析,支持多种分隔符的使 ...

  2. AI工具推荐——open-interpreter

    前言 Open Interpreter 是一个能让大型语言模型在你本地电脑上运行代码的工具. 简单来说: 它提供了一个类似于 ChatGPT 的自然语言界面,让你能通过代码与电脑互动. 你可以用它来: ...

  3. Windows的MySQL数据库升级(解压包方式)

    1.背景描述 原来的 MySQL 在安装时,是最新的稳定版本 5.7.33 . 经过一段时间后,在原来的 MySQL 版本中,发现存在漏洞. 因为 MySQL 的官方补丁,需要 Oracle 的 si ...

  4. [记录点滴] 使用工具和命令对redis数据进行备份恢复

    [记录点滴] 使用工具和命令对redis数据进行备份恢复 0x00 摘要 本文记录了如何使用工具对redis数据进行恢复备份,涉及的有Redis-Dump,MySQL,Redis管道命令. 0x01 ...

  5. Luogu P1930 亚瑟王的宫殿 题解 [ 蓝 ] [ 分层图最短路 ] [ 枚举 ]

    亚瑟王的宫殿:比较 tricky 的图论. 图论做法 思路 因为是无向图,所以考虑一个经典 trick,把所有点到集合点的距离之和化为集合点到其他所有点的位置之和,就可以从集合点做单源最短路了. 于是 ...

  6. Windows系统安装Ollama超简教程(附DeepSeek R1实战)

    一.Ollama下载指引 官网地址:https://ollama.com/download 选择Windows版本直接下载(推荐64位系统),安装包745MB左右,支持Win10/11系统.点击&qu ...

  7. JUC并发—8.并发安全集合一

    大纲 1.JDK 1.7的HashMap的死循环与数据丢失 2.ConcurrentHashMap的并发安全 3.ConcurrentHashMap的设计介绍 4.ConcurrentHashMap的 ...

  8. MDK Debug时No target connected,STM32 ST-LINK Utility连接不上单片机的解决办法“Can not connect to target!”

    芯片下载程序成功,再次下载时出现,以下错误. 点击确认后,如下提示. 或提示如下. 不管怎么设置都侦测不到芯片. 使用STM32 ST-LINK Utility连接单片机时提示下边错误 "C ...

  9. Docker安装与镜像加速器的配置

    Docker简介 百科说:Docker 是一个开源的应用容器引擎,让开发者可以打包他们的应用以及依赖包到一个可移植的容器中,然后发布到任何流行的Linux机器上,也可以实现虚拟化,容器是完全使用沙箱机 ...

  10. mysql 无数据插入,有数据更新

    mysql的语法与sql server有很多不同,sql server执行插入更新时可以update后使用if判断返回的@@rowcount值,然后确定是否插入,mysql在语句中无法使用类似sql  ...