В данном руководстве, мы построим Рекуррентную Нейронную Сеть (Recurrent Neural Network, далее — RNN) в PyTorch, которая будет классифицировать имена людей по их языкам. Предположим, что у читателя есть основы понимания PyTorch и машинного обучения в Python.
В конце данного руководства, мы сможем предугадывать язык на котором разговаривает человек по его имени.
Набор данных имён, приведенный в данном руководстве можно скачать здесь: data.zip
Это руководство — адаптация официальной документации PyTorch. Вы можете узнать больше из документации.
На сайте будет вот такая форма, где вы сможете выбрать свою операционную систему, менеджер пакетов, версия Python (Есть даже для самого нового Python 3.7) и версия CUDA (можно выбрать None если он не установлен). В зависимости от вашего выбора, вы получите инструкцию по установке.
Предварительная обработка данных
Как и в случае с любой другой задачей для машинного обучения, мы начнем с загрузки и подготовки нашего набора данных. После загрузки набора данных, обратите внимание на то, что в папке с данными есть папка под названием names. Она содержит текстовые файлы с фамилиями на восемнадцати разных именах.
Чтобы загрузить все файлы одним махом, мы используем модуль Python под названием glob. Модуль glob находит все совпадения названий путей по особому шаблону, в соответствии с правилами, используемыми в оболочке Unix. Результаты возвращаются в произвольном порядке. Мы используем его для загрузки всех файлов с окончанием .txt в папку.
Python
1
2
3
4
importglob
all_text_files=glob.glob('data/names/*.txt')
print(all_text_files)
В данный момент, названия находятся в формате Unicode. Однако, нам нужно конвертировать их в стандарт ASCII. Это поможет с удалением диакритиков в словах. Например, французское имя B?ringer будет конвертировано в Beringer.
Python
1
2
3
4
5
6
7
8
9
10
11
12
13
14
importunicodedata
importstring
all_letters=string.ascii_letters+" .,;'"
n_letters=len(all_letters)
defunicode_to_ascii(s):
return''.join(
cforcinunicodedata.normalize('NFD',s)
ifunicodedata.category(c)!='Mn'
andcinall_letters
)
print(unicode_to_ascii('B?ringer'))
Следующий шаг — создание словаря со списком имен для каждого языка.
Python
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
category_languages={}
all_categories=[]
defreadLines(filename):
lines=open(filename).read().strip().split(' ')
return[unicode_to_ascii(line)forline inlines]
forfilename inall_text_files:
category=filename.split('/')[-1].split('.')[0]
all_categories.append(category)
languages=readLines(filename)
category_languages[category]=languages
no_of_languages=len(all_categories)
print('There are {} langauages'.format(no_of_languages))
Мы можем ознакомиться с первыми 15 именами во французском словаре, как показано ниже.
Превращение имен в тензоры PyTorch
Работая с данными в PyTorch, нам нужно конвертировать их в тензоры PyTorch. Это очень похоже на массивы NumPy. В нашем случае, нам нужно конвертировать каждую букву в тензор torch. Это будет один вектор, наполненный нулями, за исключением единицы в индексе текущей буквы. Посмотрим, как это работает, и затем конвертируем M в один вектор.
Python
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
importtorch
importstring
all_letters=string.ascii_letters+" .,;'"
n_letters=len(all_letters)
defletter_to_tensor(letter):
tensor=torch.zeros(1,n_letters)
letter_index=all_letters.find(letter)
tensor[0][letter_index]=1
returntensor
defline_to_tensor(line):
tensor=torch.zeros(len(line),1,n_letters)
forli,letter inenumerate(line):
letter_index=all_letters.find(letter)
tensor[li][0][letter_index]=1
returntensor
Чтобы сформировать одно имя, нам нужно соединить несколько векторов, чтобы создать двухмерную матрицу.
Создание RNN
При создании нейронной сети в PyTorch, мы используем torch.nn.Module, который является основным классом для всех модулей нейронных сетей. В то же время, torch.autograd предоставляет классы и функции, реализующие автоматическое дифференцирование произвольных скалярных функций. И torch.nn.LogSoftmax применяет функцию Log(Softmax(x)) к n-мерному тензору ввода.
Чтобы получить вероятность для каждого языка, мы используем Tensor.topk для получения индекса наибольшего значения.
Python
1
2
3
4
5
6
defcategory_from_output(output):
top_n,top_i=output.data.topk(1)
category_i=top_i[0][0]
returnall_categories[category_i],category_i
print(category_from_output(output))
Далее, нам нужен быстрый способ получить имя и его выдачу (output). На данный момент у нас такой файл main.py (мы выделили участки кода которые были добавлены):
Следующий шаг – определение функции loss и создание оптимизатора, который будет обновлять параметры модели, в соответствии с её градиентами. Мы также устанавливаем скорость обучения нашей модели.
Мы двигаемся дальше, и определяем функцию, которая будет создавать тензоры ввода и вывода, сравним итоговую выдачу с целевой, и наконец, выполним обратное распространение (back-propagation).
Python
1
2
3
4
5
6
7
8
9
10
11
12
13
deftrain(category_tensor,line_tensor):
rnn.zero_grad()
hidden=rnn.init_hidden()
foriinrange(line_tensor.size()[0]):
output,hidden=rnn(line_tensor[i],hidden)
loss=criterion(output,category_tensor)
loss.backward()
optimizer.step()
returnoutput,loss.data[0]
Следующий шаг – это запуск нескольких примеров, используя тренировочную функцию, поскольку мы отслеживаем потери для последующего построения графика.
Мы создаем графики на основе результатов при помощи pyplot от Matplotlib. График покажет нам скорость обучения нашей нейронной сети.
Python
1
2
3
4
5
6
7
importmatplotlib.pyplot asplt
importmatplotlib.ticker asticker
%matplotlibinline
plt.figure()
plt.plot(all_losses)
Оценка результатов
Мы создадим запутанную матрицу, чтобы увидеть, как нейронная сеть проявляет себя в разных категориях. Яркие точки на основной оси показывают языки, которые были выбраны неправильно.
Если вы хотите узнать побольше о PyTorch, есть масса руководств в официальной документации. Если вы хотите узнать больше о RNN, Брайан Мванги написал фантастический гайд на эту тему.