So schreiben Sie mit PyTorch ein wiederkehrendes neuronales Netzwerk zur Klassifizierung von Zeichenfolgen

Download-Adresse des Datensatzes:

https://download.pytorch.org/tutorial/data.zip​download.pytorch.org

Der Datensatz enthält 18 Dateien mit gebräuchlichen Namen in 18 Ländern. Jede Zeile ist eine Zeichenfolge gebräuchlicher Namen im aktuellen Land.

Wir müssen ein wiederkehrendes neuronales Netzwerk (RNN) aufbauen, eine Namenszeichenfolge in das RNN eingeben und uns vom RNN sagen lassen, zu welchem ​​Land der Name gehört (Klassifizierungsproblem).

Führen Sie den folgenden Code aus und stellen Sie sicher:

PyTorch=1.9.0

Torchtext=0.10.0

from __future__ import unicode_literals, print_function, division
from io import open
import glob
import os


# 输入一串名字字符串
# 然后判断这个名字属于哪个国家

# 寻找指定的文件
def findFiles(path):
    # 遍历指定路径下的所有文件
    # 以列表的形式返回
    return glob.glob(path)


# 读取数据集
print('所有的数据集文件', findFiles('./data/names/*.txt'))
import unicodedata
import string

# 获取所有ASCII码字符,再加上'.,;'
all_letters = string.ascii_letters + " .,;'"
n_letters = len(all_letters)


# 将一个Unicode字符串(数据集中的)转换为一个ASCII字符串(输入模型中的)
# 数据标准化
# 一个Unicode字符可以用多种不同的ASCII字符表示
# 转换为统一的形式方便模型处理
def unicodeToAscii(s):
    return ''.join(
        # normalize() 第一个参数指定字符串标准化的方式。
        # NFC表示字符使用单一编码优先,
        # 而NFD表示字符应该分解为多个组合字符表示
        # 先将输入的字符转换
        # 然后再过滤
        c for c in unicodedata.normalize('NFD', s)
        # Mn表示Mark
        # 如果不是特殊标记(我们要分类人名)
        # 并且在ASCII中(利用ASCII表示Unicode)
        if unicodedata.category(c) != 'Mn'
        and c in all_letters
    )


print('Ślusàrski的ASCII编码为:', unicodeToAscii('Ślusàrski'))
# 类别->包含的所有字符串的字典
category_lines = {}
# 所有的字符串类别(国家,数据集中共有18个国家)
all_categories = []


def readLines(filename):
    # 首先open以utf-8编码打开文件
    # 然后read()读取
    # strip()去除空格
    # split('\n')分割出每一行
    lines = open(filename, encoding='utf-8').read().strip().split('\n')
    # 将每一行(也就是每一个字符串)转换为ASCII表示
    return [unicodeToAscii(line) for line in lines]


# 处理数据集
for filename in findFiles('data/names/*.txt'):
    # filename是一个文件的路径,类似于data/names/English.txt这样
    # os.paht.basename()获取文件名,也就是English.txt
    # os.path.splitext()将English.txt分解为English txt
    # os.path.splitext()[0]获取到English
    # 因为我们要完成输入一个字符串,然后判断它是哪国的名字
    # 所以English就是类别标签
    category = os.path.splitext(os.path.basename(filename))[0]
    # 建立所有类别的列表
    all_categories.append(category)
    lines = readLines(filename)
    # 建立字典
    category_lines[category] = lines
n_categories = len(all_categories)
import torch


def letterToIndex(letter):
    # 寻找输入的字符在我们建立的 ASCII 表中为索引
    # 类似于 'a'=0
    return all_letters.find(letter)


def letterToTensor(letter):
    # 建立输入的one_hot向量
    tensor = torch.zeros(1, n_letters)
    tensor[0][letterToIndex(letter)] = 1
    return tensor


def lineToTensor(line):
    # 将一个字符串都转换为one_hot的形式
    # 1可以看做批大小
    tensor = torch.zeros(len(line), 1, n_letters)
    for li, letter in enumerate(line):
        tensor[li][0][letterToIndex(letter)] = 1
    return tensor


print('J的one_hot张量:', letterToTensor('J'))
print('Jones的one_hot张量维度为:', lineToTensor('Jones').size())
import torch.nn as nn


# 搭建循环神经网络进行字符串的分类
class RNN(nn.Module):
    # 层的初始化
    def __init__(self, input_size, hidden_size, output_size):
        '''
        :param input_size: 一个名字中包含的字符的个数,比如 len(tom)
        :param hidden_size: 循环神经网络中隐藏层输出的维度
        :param output_size: 类别数
        '''
        super(RNN, self).__init__()
        self.hidden_size = hidden_size
        # 输入+h0
        self.i2h = nn.Linear(input_size + hidden_size, hidden_size)
        # 输入+h(t-1)
        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)


# 隐藏层神经元数
n_hidden = 128
# 实例化一个RNN网络
rnn = RNN(n_letters, n_hidden, n_categories)
# 测试
input = letterToTensor('A')
hidden = torch.zeros(1, n_hidden)
output, next_hidden = rnn(input, hidden)
input = lineToTensor('Albert')
hidden = torch.zeros(1, n_hidden)
output, next_hidden = rnn(input[0], hidden)
print('Albert的网络输出为:', output)


def categoryFromOutput(output):
    # 获取output中概率值最大的类别
    # top_n是值
    # top_i是索引
    top_n, top_i = output.topk(1)
    category_i = top_i[0].item()
    # 返回概率值最大的类别
    return all_categories[category_i], category_i


print('Albert的概率预测为:', categoryFromOutput(output))
import random


def randomChoice(l):
    # 从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)
# 损失函数
# 负极大似然损失函数,用于处理分类问题
# 为什么这里处理分类问题没用常用的CrossEntropy()
# 而是用这个
# 因为在前面我们定义RNN的时候里面定义了一个SoftMax层
# NLLLoss()+LogSoftmax()=CrossEntropy()
criterion = nn.NLLLoss()
# 学习率
learning_rate = 0.005


# 训练网络
def train(category_tensor, line_tensor):
    '''

    :param category_tensor: 类别张量
    :param line_tensor: 字符串张量
    :return:
    '''
    # h0
    hidden = rnn.initHidden()
    # 网络梯度清零
    rnn.zero_grad()
    # 循环
    # 我们需要最后一个output做分类
    for i in range(line_tensor.size()[0]):
        output, hidden = rnn(line_tensor[i], hidden)
    # 计算损失
    loss = criterion(output, category_tensor)
    # 反向传播
    loss.backward()
    # 根据上一步计算的loss梯度
    # 更新网络中的参数
    for p in rnn.parameters():
        p.data.add_(p.grad.data, alpha=-learning_rate)
    return output, loss.item()


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)


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
# 绘制图像
import matplotlib.pyplot as plt
import matplotlib.ticker as ticker

plt.figure()
plt.plot(all_losses)
confusion = torch.zeros(n_categories, n_categories)
n_confusion = 10000


# 利用 RNN 进行预测
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()
# 进行绘图
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))
plt.show()


# 利用网络进行预测
def predict(input_line, n_predictions=3):
    print('\n> %s' % input_line)
    with torch.no_grad():
        output = evaluate(lineToTensor(input_line))
        topv, topi = output.topk(n_predictions, 1, True)
        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')

Supongo que te gusta

Origin blog.csdn.net/Talantfuck/article/details/124566841
Recomendado
Clasificación