一文详解名字分类(字符级RNN)

news2024/9/23 17:16:28

目录

一.前言

二.数据预处理

三.构造神经网络

四.训练

五.评价结果(预测)


一.前言

我们将构建和训练字符级RNN来对单词进行分类。字符级RNN将单词作为一系列字符读取,在每一步输出预测和“隐藏状态”,将其先前的隐藏 状态输入至下一时刻。我们将最终时刻输出作为预测结果,即表示该词属于哪个类。

具体来说,我们将在18种语言构成的几千个名字的数据集上训练模型,根据一个名字的拼写预测它是哪种语言的名字:

$ python predict.py Hinton
(-0.47) Scottish
(-1.52) English
(-3.57) Irish

$ python predict.py Schmidhuber
(-0.19) German
(-2.48) Czech
(-2.68) Dutch

项目结构:

二.数据预处理

点击这里下载数据,并将其解压到当前文件夹。

在"data/names"文件夹下是名称为"[language].txt"的18个文本文件。每个文件的每一行都有一个名字,它们几乎都是罗马化的文本 (但是我们仍需要将其从Unicode转换为ASCII编码)

我们最终会得到一个语言对应名字列表的字典,{language: [names ...]}。通用变量“category”和“line”(例子中的语言和名字单词) 用于以后的可扩展性。

下面的的处理数据的代码把上篇文章的数据预处理代码和训练前的张量转换代码算是汇总了一下:

dataPreprocessing.py
from __future__ import unicode_literals, print_function, division
from io import open
import glob
import os
import unicodedata
import string

import torch

class DataPreprocessing:
    def __init__(self):
        self.all_letters = string.ascii_letters + " .,;'-"  # 注意还有空格
        # print('string.ascii_letters:', string.ascii_letters)  # 大小写的26个字母
        # print('all_letters:', self.all_letters)
        self.n_letters = len(self.all_letters) + 1  # Plus EOS marker
        # print('总的字符数量:', self.n_letters)

    def findFiles(self,path):
        # glob.glob返回符合匹配条件的所有文件的路径,即路径中可以用正则表达式
        return glob.glob(path)

    # 将Unicode字符串转换为纯ASCII, 感谢https://stackoverflow.com/a/518232/2809427
    def unicodeToAscii(self,s):
        return ''.join(
            c for c in unicodedata.normalize('NFD', s)
            if unicodedata.category(c) != 'Mn'
            and c in self.all_letters
        )

    # 读取文件并分成几行
    def readLines(self,filename):
        # strip()返回删除前导和尾随空格的字符串副本
        lines = open(filename, encoding='utf-8').read().strip().split('\n')
        return [self.unicodeToAscii(line) for line in lines]

    def processing(self):
        # 构建category_lines字典,列表中的每行是一个类别
        category_lines = {}
        all_categories = []
        for filename in self.findFiles('data/names/*.txt'):
            # print(filename) filename是一个路径
            category = os.path.splitext(os.path.basename(filename))[0]
            all_categories.append(category)
            lines = self.readLines(filename)
            category_lines[category] = lines

        n_categories = len(all_categories)

        if n_categories == 0:
            raise RuntimeError('Data not found. Make sure that you downloaded data '
                               'from https://download.pytorch.org/tutorial/data.zip and extract it to '
                               'the current directory.')

        return category_lines,all_categories,n_categories,self.all_letters,self.n_letters;


data=DataPreprocessing()
#返回值类型分别是 字典,列表,整数,字符串,整数
'''
现在我们有了category_lines,一个字典变量存储每一种语言及其对应的每一行文本(名字)列表的映射关系。
变量all_categories是全部 语言种类的列表,变量n_categories是语言种类的数量,后续会使用。
'''
category_lines,all_categories,n_categories,all_letters,n_letters=data.processing()
#print(category_lines['Italian'][:5])

'''
单词转变为张量
现在我们已经加载了所有的名字,我们需要将它们转换为张量来使用它们。
我们使用大小为<1 x n_letters>的“one-hot 向量”表示一个字母。一个one-hot向量所有位置都填充为0,并在其表示的字母的位置表示为1,
例如"b" = <0 1 0 0 0 ...>.(字母b的编号是2,第二个位置是1,其他位置是0)
我们使用一个<line_length x 1 x n_letters>的2D矩阵表示一个单词,line_length是单词的长度,即此单词包含多少字符
额外的1维是batch的维度,PyTorch默认所有的数据都是成batch处理的。我们这里只设置了batch的大小为1。
'''
# 从all_letters中查找字母索引,例如 "a" = 0
def letterToIndex(letter):
    return all_letters.find(letter)

# 仅用于演示,将字母转换为<1 x n_letters> 张量
def letterToTensor(letter):
    tensor = torch.zeros(1, n_letters)
    tensor[0][letterToIndex(letter)] = 1
    return tensor

# 将一个单词(即名字)转换为<line_length x 1 x n_letters>,或一个0ne-hot字母向量的数组
def lineToTensor(line):
    tensor = torch.zeros(len(line), 1, n_letters)
    for li, letter in enumerate(line):
        tensor[li][0][letterToIndex(letter)] = 1
    return tensor

# print(letterToTensor('J'))
# print(lineToTensor('Joy'))
# print(lineToTensor('Joy').size())

三.构造神经网络

在autograd之前,要在Torch中构建一个可以复制之前时刻层参数的循环神经网络。layer的隐藏状态和梯度将交给计算图自己处理。这意味着 你可以像实现的常规的 feed-forward 层一样,以很纯粹的方式实现RNN。

这个RNN组件 (几乎是从这里复制的the PyTorch for Torch users tutorial) 仅使用两层 linear 层对输入和隐藏层做处理,在最后添加一层 LogSoftmax 层预测最终输出。

model.py

import torch
import torch.nn as nn

from dataPreprocessing import n_letters,n_categories

class RNN(nn.Module):
    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, hidden):
        combined = torch.cat((input, hidden), 1)
        hidden = self.i2h(combined)
        output = self.i2o(combined)
        output = self.softmax(output)
        return output, hidden

    def initHidden(self):
        return torch.zeros(1, self.hidden_size)

四.训练

代码中的每句注释都要好好看,精华!!!想要完全理解分段解开相应的代码输出看看是非常有效的办法。

这个项目的很多代码都和上篇文章(字符级RNN生成名字)差不多,所以很多地方就不注释了,上篇已详细注释。

train.py

import torch
from torch import nn

from model import RNN
from dataPreprocessing import n_letters,n_categories,letterToTensor,lineToTensor,all_categories,category_lines

import random

#########################################  训练前的准备  #################################################
n_hidden = 128
#传入的参数分别是字符的总个数,隐藏状态向量的维度,名字(所属国家)种类的个数
rnn = RNN(n_letters, n_hidden, n_categories)

'''
要运行此网络的一个步骤,我们需要传递一个输入(在我们的例子中,是当前字母的Tensor)和一个先前隐藏的状态(我们首先将其初始化为零)。 
我们将返回输出(每种语言的概率)和下一个隐藏状态(为我们下一步保留使用)。
'''
# input = letterToTensor('A')
# hidden =torch.zeros(1, n_hidden)
# output, next_hidden = rnn(input, hidden)
#print(output.shape) # torch.Size([1, 18])
#print(next_hidden.shape)# torch.Size([1, 128])
#print(next_hidden)

'''
为了提高效率,我们不希望为每一步都创建一个新的Tensor,因此我们将使用lineToTensor函数而不是letterToTensor函数,并使用切片方法。
这一步可以通过预先计算批量的张量进一步优化。
'''
# input = lineToTensor('Albert')
# print('input.shape:',input.shape)
# print('input[0].shape:',input[0].shape)
# hidden = torch.zeros(1, n_hidden)
# output, next_hidden = rnn(input[0], hidden)

#可以看到输出是一个<1 x n_categories>的张量,其中每一条代表这个单词属于某一类的可能性(越高可能性越大)。
# print('input[0]各类别的可能性:',output)

'''
进行训练步骤之前我们需要构建一些辅助函数。 * 第一个是当我们知道输出结果对应每种类别的可能性时,解析神经网络的输出。
我们可以使用 Tensor.topk函数得到最大值在结果中的位置索引:
'''
#从output中获取对应的种类与下标
def categoryFromOutput(output):
    #top_n是output中数值最大的那个,top_i是对应的下标(从0开始)
    top_n, top_i = output.topk(1)
    # print('top_n:',top_n)
    # print('top_i:',top_i)
    category_i = top_i[0].item()
    return all_categories[category_i], category_i

# print(categoryFromOutput(output))

# 第二个是我们需要一种快速获取训练示例(得到一个名字及其所属的语言类别)的方法:
def randomChoice(l):
    return l[random.randint(0, len(l) - 1)]

def randomTrainingExample():
    category = randomChoice(all_categories)
    line = randomChoice(category_lines[category])
    #返回种类对应的下标(张量形式)
    category_tensor = torch.tensor([all_categories.index(category)], dtype=torch.long)
    line_tensor = lineToTensor(line)
    return category, line, category_tensor, line_tensor

# for i in range(10):
#     category, line, category_tensor, line_tensor = randomTrainingExample()
#     print('category =', category, '/ line =', line)

#############################################  训练神经网络  #####################################################
# 现在,训练过程只需要向神经网络输入大量的数据,让它做出预测,并将对错反馈给它。
# nn.LogSoftmax作为最后一层layer时,nn.NLLLoss作为损失函数是合适的。
criterion = nn.NLLLoss()
'''
训练过程的每次循环将会发生:
    .构建输入和目标张量
    .构建0初始化的隐藏状态
    .读入每一个字母
         * 将当前隐藏状态传递给下一字母
    .比较最终结果和目标
    .反向传播
    .返回结果和损失
'''
learning_rate = 0.005 # If you set this too high, it might explode(爆炸). If too low, it might not learn
#line_tensor就是一个单词对应的向量
def train(category_tensor, line_tensor):

    hidden = rnn.initHidden()
    #梯度清零
    rnn.zero_grad()
    #前向传播
    for i in range(line_tensor.size()[0]):
        output, hidden = rnn(line_tensor[i], hidden)
    #损失
    loss = criterion(output, category_tensor)
    #反向传播,计算梯度
    loss.backward()
    #更新权重
    # 将参数的梯度添加到其值中,乘以学习速率
    for p in rnn.parameters():
        p.data.add_(-learning_rate, p.grad.data)

    return output, loss.item()

# 现在我们只需要准备一些例子来运行程序。由于train函数同时返回输出和损失,我们可以打印其输出结果并跟踪其损失画图。
# 由于有1000个示例,我们每print_every次打印样例,并求平均损失。
import time
import math

n_iters = 100000
print_every = 5000
plot_every = 1000

# 跟踪绘图的损失
current_loss = 0
all_losses = []

def timeSince(since):
    now = time.time()
    s = now - since
    m = math.floor(s / 60)
    s -= m * 60
    return '%dm %ds' % (m, s)

'''
因为下面的代码没写在函数中,而且predict.py又从train.py里导了包,所以执行predict.py里的代码时,
train.py里的代码也会被执行,所以当训练完毕模型参数保存后要把下面的代码注释掉,需要训练时再重新解开运行
'''
# start = time.time()
# 
# for iter in range(1, n_iters + 1):
#     category, line, category_tensor, line_tensor = randomTrainingExample()
#     output, loss = train(category_tensor, line_tensor)
#     current_loss += loss
# 
#     # 打印迭代的编号,损失,名字和猜测
#     if iter % print_every == 0:
#         guess, guess_i = categoryFromOutput(output)
#         correct = '✓' if guess == category else '✗ (%s)' % category
#         print('%d  %d%% (%s) %.4f  %s / %s  %s' % (iter, iter / n_iters * 100, timeSince(start), loss, line, guess, correct))
# 
#     # 将当前损失平均值添加到损失列表中
#     if iter % plot_every == 0:
#         all_losses.append(current_loss / plot_every)
#         current_loss = 0
# 
# # 画图:从all_losses得到历史损失记录,反映了神经网络的学习情况:
# import matplotlib.pyplot as plt
# 
# plt.figure()
# plt.plot(all_losses)
# plt.show()
# 
# #保存模型
# torch.save(rnn.state_dict(), './model/myRNN.pth')

五.评价结果(预测)

predict.py

import torch
from matplotlib import pyplot as plt, ticker

from dataPreprocessing import n_categories,all_categories,n_letters
from train import randomTrainingExample,categoryFromOutput,lineToTensor
from model import RNN


n_hidden = 128
#传入的参数分别是字符的总个数,隐藏状态向量的维度,名字(所属国家)种类的个数
rnn = RNN(n_letters, n_hidden, n_categories)
#加载已经训练好的模型参数
rnn.load_state_dict(torch.load('./model/myRNN.pth'))
#eval函数(一定用!!!)的作用请参考 https://blog.csdn.net/lgzlgz3102/article/details/115987271
rnn.eval()

'''
为了了解网络在不同类别上的表现,我们将创建一个混淆矩阵,显示每种语言(行)和神经网络将其预测为哪种语言(列)。
为了计算混淆矩 阵,使用evaluate()函数处理了一批数据,evaluate()函数与去掉反向传播的train()函数大体相同。
'''
# 在混淆矩阵中跟踪正确的猜测
confusion = torch.zeros(n_categories, n_categories)
n_confusion = 10000

# 只需返回给定一行的输出,即对一个人名的预测结果(1*n_categories的二维矩阵)
def evaluate(line_tensor):
    hidden = rnn.initHidden()

    for i in range(line_tensor.size()[0]):
        output, hidden = rnn(line_tensor[i], hidden)

    return output

# 查看一堆正确猜到的例子和记录
for i in range(n_confusion):
    #标签
    category, line, category_tensor, line_tensor = randomTrainingExample()
    #预测结果
    output = evaluate(line_tensor)
    guess, guess_i = categoryFromOutput(output)
    category_i = all_categories.index(category)
    confusion[category_i][guess_i] += 1

# 通过将每一行除以其总和来归一化
for i in range(n_categories):
    confusion[i] = confusion[i] / confusion[i].sum()
# print(confusion[:3])
# print(confusion[17])

# 设置绘图
'''
你可以从主轴线以外挑出亮的点,显示模型预测错了哪些语言,例如汉语预测为了韩语,西班牙预测为了意大利。
看上去在希腊语上效果很好, 在英语上表现欠佳。(可能是因为英语与其他语言的重叠较多)。
'''
fig = plt.figure()
ax = fig.add_subplot(111)
cax = ax.matshow(confusion.numpy())
fig.colorbar(cax)

# 设置轴
ax.set_xticklabels([''] + all_categories, rotation=90)
ax.set_yticklabels([''] + all_categories)

# 每个刻度线强制标签
ax.xaxis.set_major_locator(ticker.MultipleLocator(1))
ax.yaxis.set_major_locator(ticker.MultipleLocator(1))

# sphinx_gallery_thumbnail_number = 2
plt.show()


#处理用户输入
def predict(input_line, n_predictions=3):
    print('\n> %s' % input_line)
    with torch.no_grad():
        output = evaluate(lineToTensor(input_line))

        # 获得前N个类别
        # topk函数参考 https://blog.csdn.net/qq_38156104/article/details/109318702
        topv, topi = output.topk(n_predictions, 1, True)
        print('topv:',topv)
        print('topi:', topi)
        # predictions = []

        for i in range(n_predictions):
            #概率
            value = topv[0][i].item()
            #索引
            category_index = topi[0][i].item()
            print('(%.2f) %s' % (value, all_categories[category_index]))
            # predictions.append([value, all_categories[category_index]])

predict('Dovesky')
predict('Jackson')
predict('Satoshi')

输出结果:

> Dovesky
topv: tensor([[-0.5240, -1.3990, -2.5749]])
topi: tensor([[ 2, 14,  4]])
(-0.52) Czech
(-1.40) Russian
(-2.57) English

> Jackson
topv: tensor([[-0.7963, -1.4187, -1.6734]])
topi: tensor([[15,  3,  4]])
(-0.80) Scottish
(-1.42) Dutch
(-1.67) English

> Satoshi
topv: tensor([[-0.9223, -1.0976, -2.0812]])
topi: tensor([[10,  0,  9]])
(-0.92) Japanese
(-1.10) Arabic
(-2.08) Italian

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

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

相关文章

02-MySQL数据管理

目录 DDL&#xff08;数据操作语言&#xff09; 添加数据 添加student表数据 修改数据 WHERE条件子句 修改student表数据 删除数据 删除student表数据 总结&#xff1a; DDL&#xff08;数据操作语言&#xff09; 用于操作数据库对象中所包含的数据 关键字&#xff1…

STM32的光敏检测自动智能窗帘控制系统proteus设计

STM32的光敏检测自动智能窗帘控制系统proteus设计 ( proteus仿真程序演示视频&#xff09; 仿真图proteus 8.9 程序编译器&#xff1a;keil 5 编程语言&#xff1a;C语言 设计编号&#xff1a;C0074 主要功能&#xff1a; 结合实际情况&#xff0c;基于STM32单片机设计一…

ATTCK-T1078-001-默认账户

0x01基础信息 具体信息详情ATT&CK编号T1078-001所属战术阶段初始访问操作系统windows 7 旗舰版 SP1创建时间2022年11月10日监测平台火绒安全、火绒剑、sysmon 0x02技术原理 攻击者可能会获取和滥用默认帐户的凭据&#xff0c;以作为获得初始访问、持久性、特权升级或防御…

Python用一行代码,截取图片

前言 本文是该专栏的第3篇,后面会持续分享python的各种黑科技知识,值得关注。 在工作上,有些时候可能需要用到代码来进行自动截图,比如说需要图文识别,进行数据信息抽取的时候,能自动定位截图无疑是很好的办法。 对python而言,截图方法其实有很多,但笔者下面要介绍的…

活动明天见 | DataFunSummit 2022 AI基础软件架构峰会圆桌会

11月16日晚 19&#xff1a;30-21:00&#xff0c;第四范式技术副总裁、OpenMLDB 项目发起人郑曌受邀主持DataFunSummit 2022 AI基础软件架构峰会圆桌会&#xff0c;将与各位资深专家在线上做深度的交流分享&#xff0c;欢迎大家届时收看。 开源机器学习数据库 OpenMLDB **活动时…

全国产三防加固计算机

国产三防加固计算机&#xff0c;本文指的是CPU为国产飞腾D2000处理器、操作系统为国产麒麟V10 SP1系统&#xff0c;整机方案采用全国产化设计。 达梦数据库是由武汉达梦数据库股份有限公司推出的具有完全自主知识产权的高性能数据库管理系统&#xff0c;简称DM。达梦数据库管理…

“辣条一哥”卫龙冲击港股IPO,我又吃出一家上市公司

一、公司简介 卫龙作为国内领先的辣味休闲食品企业&#xff0c;是目前国内辣味食品当之无愧的第一品牌。据2021年零售销售额计&#xff0c;公司在辣味休闲食品企业中排名第一&#xff0c;市场份额达到了6.2%&#xff0c;是一款备受年轻消费者喜爱的休闲食品品牌。天眼查App显示…

vue3之实现响应式数据ref和reactive

用途 ref、reactive都是vue3提供实现响应式数据的方法 ref() 接受一个内部值&#xff0c;返回一个响应式的、可更改的ref对象&#xff0c;此对象只有一个指向其内部的属性.value ref可以说是简化版的reactive&#xff0c;与reactive的区别则是 ref是对某一个数据类型的单独…

测试工程师面试题

很多软件测试工程师在面试的时候都会遇到考官给的各种各样的面试题&#xff0c;这也反应了测试工程师对企业的重要性&#xff0c;面试通常分为以下几个方面&#xff0c;由于篇幅有限&#xff0c;在这里就只给大家分享一些比较常见的问题。 01 自我介绍 这里我不分享如何自我介…

js 网格卡片交换动画

关键技术&#xff1a;animate(keyframes, options) keyframes 关键帧对象数组&#xff0c;或一个关键帧对象&#xff08;其属性为可迭代值的数组&#xff09; 代表动画持续时间的整数&#xff08;以毫秒为单位&#xff09;&#xff0c;或者一个包含一个或多个时间属性 参考传送…

C# 学习之路(类的基本概念)

C# 学习之路&#xff08;类的基本概念&#xff09; 类的概述 类是一种活动的数据结构 程序的数据和功能被组织为逻辑上相关的数据项和函数封装的集合&#xff0c;并称为类 类包含数据成员和函数成员&#xff1a; 数据成员&#xff1a;存储与类或类的实例相关的数据&#xff08…

Linux条件变量

一、条件变量函数 1&#xff0c;条件变量 条件变量&#xff1a;用来描述某种临界资源是否就绪的一种数据化描述。通常要配合mutex一起使用。 2&#xff0c;初始化 pthread_connd_init: 同样有一个静态的和一个动态的。 其中cond是需要初始化的条件变量&#xff0c;attr传…

在线书法培训迎来新风口

随着社会教育水平的不断提高&#xff0c;书法不再是独属于某个年龄层的爱好&#xff0c;开始被越来越多人欣赏和重视。 写一手好字&#xff0c;对每一个人来说都是一个加分“外挂”。在考场上&#xff0c;一手好字无疑可以让阅卷老师在心中自然加分&#xff1b;在职场中&#…

源码深度解析之 Spring IOC

1. 基础知识 1.1 什么是 Spring IOC &#xff1f; IOC 不是一种技术&#xff0c;只是一种思想&#xff0c;一个重要的面向对象编程的法则&#xff0c;它能指导我们如何设计出松耦合、更优良的程序。 传统应用程序都是由我们在类内部主动创建依赖对象&#xff0c;从而导致类与…

Vue--》自定义指令的使用讲解

目录 自定义指令 自定义指令的分类 私有自定义指令&#xff1a; 全局自定义指令&#xff1a; 总结 自定义指令 vue官方提供了v-text、v-for、v-model、v-if等常用的指令&#xff0c;还允许开发者自定义指令。 自定义指令的分类 私有自定义指令&#xff1a; 在每个vue组…

[附源码]计算机毕业设计框架的食品安全监督平台的设计与实现Springboot程序

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…

u-BOOT启动流程

1、u-boot入口&#xff1a;入口即第一行程序&#xff0c;程序的链接是由链接脚本决定的&#xff0c;编译uboot之后&#xff0c;可以在根目录下生成u-boot.lds文件 打开lds文件可以看到代码当前入口点&#xff1a; &#xff1a;_start&#xff0c;_start后面就是中断向量表&…

[附源码]Python计算机毕业设计Django演唱会门票售卖系统

项目运行 环境配置&#xff1a; Pychram社区版 python3.7.7 Mysql5.7 HBuilderXlist pipNavicat11Djangonodejs。 项目技术&#xff1a; django python Vue 等等组成&#xff0c;B/S模式 pychram管理等等。 环境需要 1.运行环境&#xff1a;最好是python3.7.7&#xff0c;…

股票接口怎样获取数据方便?

目前很多交易者对股票数据行情查询相关使用的系统也是很普遍&#xff0c;就比如说你查询A股的大秦铁路可以使用实时股票数据接口来获取它的实时股价&#xff0c;成交量&#xff0c;成交金额等情况。那么在这个过程中&#xff0c;股票接口是如何获取数据更方便呢&#xff1f;小编…

化工厂人员定位系统:聚焦化工安全问题,提升智慧管理水平

化工厂人员定位系统&#xff0c;以人员定位技术为核心&#xff0c;整合访客管理、出入口控制、地图、人脸识别、视频监控数据采集等各种管控技术&#xff0c;提供精准实时定位、轨迹查询、货物跟踪等功能&#xff0c;又称“智能化二道门人员管理系统”。 12月1日&#xff0c;鞍…