Машинне Навчання: Класифікація тексту з Нейронними Мережами та TensorFlow

17 хв. читання

Розробники зазвичай кажуть, що для того, щоб почати вивчати машинне навчання, вам потрібно спочатку розуміти, як працюють алгоритми. Але, можливо, це не так.

Спочатку вам потрібно розуміти загальну картину: як працюють застосунки із застосуванням машинного навчання. З цими знаннями вам буде легше заглибитись у галузь та пізнавати внутрішні алгоритми.

То як же нам все-таки це зрозуміти? Гарним способом є створювати моделі машинного навчання.

Ми припускаємо, що ви взагалі не знаєте, як реалізовувати алгоритми, тому нам потрібно буде використовувати бібліотеку, яка вже їх має. У нашому випадку ми будемо використовувати бібліотеку TensorFlow.

У цій статті ми будемо створювати модель машинного навчання для класифікації тексту на категорії. Ми обговоримо наступні теми:

1. Як працює бібліотека TensorFlow

2. Що таке модель машинного навчання

3. Що таке Нейронна Мережа

4. Як навчати Нейронну Мережу

5. Як працювати з даними та передавати їх на вхід у Нейронну Мережу

6. Як запускати модель та отримувати результати прогнозування

TensorFlow

TensorFlow – відкрита бібліотека для машинного навчання, яка була створена компанією Google. Її назва пояснює, як нам слід з нею працювати: тензори означають багатовимірні масиви, які пропускають через вузли графу.

tf.Graph

Кожен розрахунок у TensorFlow відбувається за принципом графу потоків даних. Цей граф містить два елементи:

  • елементи tf.Operation, які репрезентують оператори
  • елементи tf.Tensor, які репрезентують дані

Щоб краще зрозуміти, приведемо наступний граф:

Машинне Навчання: Класифікація тексту з Нейронними Мережами та TensorFlow
Граф, який репрезентує додавання x+y

Оголосимо два масиви x = [1,3,6] та y = [1,1,1]. Оскільки граф використовує tf.Tensor для репрезентації даних, нам потрібно буде оголосити їх як константні тензори:

import tensorflow as tf
x = tf.constant([1,3,6]) 
y = tf.constant([1,1,1])

Тепер потрібно оголосити елемент, який репрезентує оператор:

import tensorflow as tf
x = tf.constant([1,3,6]) 
y = tf.constant([1,1,1])
op = tf.add(x,y)

Після того, як ми отримали всі елементи графу, побудуємо сам граф:

import tensorflow as tf
my_graph = tf.Graph()
with my_graph.as_default():
    x = tf.constant([1,3,6]) 
    y = tf.constant([1,1,1])
    op = tf.add(x,y)

Ось так працює TensorFlow: спочатку ви створюєте граф, і тільки після цього ви робите розрахунки (тобто «вмикаєте» вузли графу з операторами). Щоб запустити граф вам потрібно створити tf.Session.

tf.Session

Об'єкт tf.Session приховує середовище, у якому виконуються об'єкти-оператори Operation та оцінюються об'єкти-тензори Tensor (див. документацію). Щоб створити tf.Session, нам потрібно оголосити граф, який буде використовуватись у класі Session:

import tensorflow as tf
my_graph = tf.Graph()
with tf.Session(graph=my_graph) as sess:
    x = tf.constant([1,3,6]) 
    y = tf.constant([1,1,1])
    op = tf.add(x,y)

Щоб виконувати операції, виконується метод tf.Session.run(). Цей метод виконує один «крок» у розрахунку TensorFlow, запускаючи відповідні фрагменти у графі, щоб отримувати потрібні об'єкти-оператори та об'єкти-тензори через аргумент fetches. У нашому випадку потрібно запустити крок з операціями додавання:

import tensorflow as tf
my_graph = tf.Graph()
with tf.Session(graph=my_graph) as sess:
    x = tf.constant([1,3,6]) 
    y = tf.constant([1,1,1])
    op = tf.add(x,y)
    result = sess.run(fetches=op)
    print(result)
>>> [2 4 7]

Модель Прогнозування

Оскільки ми вже ознайомилися з принципами роботи TensorFlow, нам потрібно навчитися створювати модель прогнозування. Якщо коротко, то

Алгоритм машинного навчання + дані = модель прогнозування

Процес створення моделі виглядає наступним чином:

Машинне Навчання: Класифікація тексту з Нейронними Мережами та TensorFlow
Процес створення моделі прогнозування

Це означає, що модель прогнозування складається з алгоритму машинного навчання, «навченого» вхідними даними. Робота з готовою моделлю буде виглядати так:

Машинне Навчання: Класифікація тексту з Нейронними Мережами та TensorFlow
Процес прогнозування

Метою моделі, яку ми будемо далі створювати, є класифікація тексту у категорії, тобто:

на вхід: текст, результат: категорія

Ми маємо набір даних для навчання, який має весь текст з готовими категоріями (тобто до кожного фрагменту тексту прикріплено відповідний ярлик, до якої категорії він відноситься). У машинному навчанні такого виду процес називається Контрольоване навчання.

Ми знаємо правильні відповіді. Алгоритм ітеративно робить прогнозування на готових даних та перевіряється вчителем.

Нам потрібно класифікувати дані у різні категорії, тому це називається завданням класифікації. Для створення моделі ми будемо використовувати Нейронні Мережі.


Нейронні Мережі

Нейронна мережа є обчислювальною моделлю (спосіб реалізації системи, використовуючи мову математики та математичні концепти). Такі системи можуть самонавчатися та бути навченими, на відміну від інших систем, які потрібно заздалегідь програмувати.

Робота нейронних мереж схожа на роботу нашої центральної нервової системи. Вони мають з'єднані між собою вузли, які імітують нейрони у нашій голові.

Машинне Навчання: Класифікація тексту з Нейронними Мережами та TensorFlow
Нейронна мережа

Найпершим алгоритмом для нейронних мереж був Перцептрон. Ця стаття дуже добре пояснює роботу алгоритму.

Щоб краще зрозуміти принцип роботи нейронної мережі, ми побудуємо її архітектуру використовуючи TensorFlow.

Архітектура нейронної мережі

Наша нейронна мережа міститиме два приховані шари (насправді, ви самі вирішуєте, скільки прихованих шарів повинно бути у мережі). Кожен прихований шар перетворює вхідні дані на такі, які зможе далі використовувати вихідний шар.

Перший прихований шар

Машинне Навчання: Класифікація тексту з Нейронними Мережами та TensorFlow
Вхідний та перший прихований шари

Кількість вузлів у першому прихованому шарі також залежить від вас. Ці вузли також називаються особливостями або нейронами, на рисунку вони зображуються у вигляді блакитних кіл.

У вхідному шарі кожний вузол відповідає слову у наборі даних (ми побачимо, як це насправді працює, пізніше).

За Перцептроном, кожний вузол (нейрон) множиться на вагу. Кожен вузол має відповідне значення ваги, яке регулюється протягом процесу навчання, щоб отримувати правильний вихід.

Крім того, що кожний вузол множиться на його вагу, у нейронну мережу також додається зміщення (про те, яку роль грає зміщення можна прочитати тут) .

Після множення вхідних значень на ваги та додавання зміщення, отримані дані проходять через активаційну функцію. За допомогою активаційної функції отримуються фінальні значення для кожного вузла. Уявіть, що кожен вузол – це ліхтар, а активаційна функція вказує ліхтарю ввімкнутись або вимкнутись.

Існує багато видів активаційних функцій. Ми будемо використовувати функцію від назвою ReLu (rectified linear unit). Вона оголошується наступним чином:

\\[f(x) = max(0,x)\\] що означає, що результат буде або \\(x\\), або \\(0\\).

Приклади: якщо \\(x = -1\\), то \\(f(x) = 0\\); якщо \\(x = 0.7\\), то \\(f(x) = 0.7\\).

Другий прихований шар

Другий прихований шар повторюї усі дії з першого прихованого шару, різниця тільки у тому, что входом у нього є вихід першого прихованого шару.

Машинне Навчання: Класифікація тексту з Нейронними Мережами та TensorFlow
Перший та другий приховані шари

Вихідний шар

Для отримання результатів з вихідного шару ми будемо використовувати унітарний код. У такому кодуванні тільки один біт має значення 1, а всі інші – 0. Наприклад, якщо ми хочемо закодувати три категорії (спорт, космос та комп'ютерна графіка):

+-------------------+-----------+
|    category       |   value   |
+-------------------|-----------+
|      sports       |    001    |
|      space        |    010    |
| computer graphics |    100    |
|-------------------|-----------|

Отже, кількість вузлів у вихідному шарі повинна збігатися з кількістю класів у вхідному наборі даних.

Значення у вихідному шарі також множаться на ваги та додаються до зміщення, але тепер використовується інша активаційна функція.

Ми хочемо позначити кожний текст відповідною категорією та один текст не може мати дві категорії одночасно. Тому замість функції ReLu ми використаємо функцію Softmax. Ця функція перетворює кожне вхідне значення у значення в інтервалі від 0 до 1 так, щоб сума всіх значень дорівнювала 1. Таким чином вихід шару буде казати нам імовірність належності тексту кожній категорії.

| 1.2                    0.46|
| 0.9   -> [softmax] ->  0.34|
| 0.4                    0.20|

Тепер ми маємо граф потоків даних нашої нейронної мережі. Якщо перекласти це все у код, ми отримаємо:

# Параметри Мережі
n_hidden_1 = 10        # кількість вузлів у першому прихованому шарі
n_hidden_2 = 5         # кількість вузлів у другому прихованому шарі
n_input = total_words  # слова у словнику
n_classes = 3          # Категорії: графіка, космос та бейсбол
def multilayer_perceptron(input_tensor, weights, biases):
    layer_1_multiplication = tf.matmul(input_tensor, weights['h1'])
    layer_1_addition = tf.add(layer_1_multiplication, biases['b1'])
    layer_1_activation = tf.nn.relu(layer_1_addition)
# Прихований шар з активаційною функцією RELU
    layer_2_multiplication = tf.matmul(layer_1_activation, weights['h2'])
    layer_2_addition = tf.add(layer_2_multiplication, biases['b2'])
    layer_2_activation = tf.nn.relu(layer_2_addition)
# Вихідний шар з лінійною функцією активації
    out_layer_multiplication = tf.matmul(layer_2_activation, weights['out'])
    out_layer_addition = out_layer_multiplication + biases['out']
		return out_layer_addition
(Пізніше ми обговоримо код для вихідного шару детальніше.)

Як навчати нейронну мережу

Як ми вже знаємо, значення ваг змінюються, коли нейронна мережа навчається. Тепер ми поговоримо про те, як це відбувається у середовищі TensorFlow.

tf.Variable

Ваги та зміщення містяться у змінних (tf.Variable). Ці змінні змінюються під час роботи методу run(). У машинному навчанні ми, зазвичай, розподіляємо стартові ваги та зміщення за нормальним законом розподілу.

weights = {
    'h1': tf.Variable(tf.random_normal([n_input, n_hidden_1])),
    'h2': tf.Variable(tf.random_normal([n_hidden_1, n_hidden_2])),
    'out': tf.Variable(tf.random_normal([n_hidden_2, n_classes]))
}
biases = {
    'b1': tf.Variable(tf.random_normal([n_hidden_1])),
    'b2': tf.Variable(tf.random_normal([n_hidden_2])),
    'out': tf.Variable(tf.random_normal([n_classes]))
}

Для першого запуску мережі (у такому випадку значення ваг – це значення, які рандомно присвоєні за нормальним розподілом):

вхідні значення: x
ваги: w
зміщення: b
вихідні значення: z
очікувані значення: e

Для того, щоб перевірити, чи навчається нейронна мережа, вам потрібно порівняти вихідні значення (z) з очікуваними значеннями (e). І як же нам знаходити цю різницю? Існує багато методів зробити це. Оскільки нашою задачею є класифікація, то найкращою мірою для знаходження різниці буде похибка перехресної ентропії.

Ми будемо знаходити похибку перехресної ентропії за допомогою методу tf.nn.softmax_cross_entropy_with_logits() у TensorFlow, після чого знайдемо середню похибку через (tf.reduced_mean()).

# Створити модель
prediction = multilayer_perceptron(input_tensor, weights, biases)
# Знайти похибку
entropy_loss = tf.nn.softmax_cross_entropy_with_logits(logits=prediction, labels=output_tensor)
loss = tf.reduce_mean(entropy_loss)

Тепер ми хочемо знайти найкращі значення для ваг та зміщення, щоб мінімізувати вихідну похибку (різницю між значенням, яке ми отримали, та правильним значенням). Для цього ми використаємо один з методів градієнтного спуску, стохастичний градієнтний спуск.

Машинне Навчання: Класифікація тексту з Нейронними Мережами та TensorFlow
Градієнтний спуск

Існує багато алгоритмів для градієнтного спуску, ми будемо розглядати Adaptive Moment Estimation (Adam). Щоб запустити цей алгоритм у TensorFlow, потрібно передати змінну learning_rate, яка визначає величину кроку збільшення значень для знаходження оптимальних ваг.

Метод tf.train.AdamOptimizer(learning_rate).minimize(loss) називають синтаксичним цукром, оскільки він виконує дві наступні функції:

  1. compute_gradients(loss, < список змінних >) – знаходить градієнт
  2. apply_gradients(< список змінних >) – використовує градієнт

Цей метод оновлює усі змінні у tf.Variable новими значеннями. Тепер ми маємо код для тренування нашої нейронної мережі:

learning_rate = 0.001
# Створити модель
prediction = multilayer_perceptron(input_tensor, weights, biases)
# Знайти похибку
entropy_loss = tf.nn.softmax_cross_entropy_with_logits(logits=prediction, labels=output_tensor)
loss = tf.reduce_mean(entropy_loss)
optimizer = tf.train.AdamOptimizer(learning_rate=learning_rate).minimize(loss)

Управління даними

Дані, які ми будемо використовувати, мають багато тексту англійською мовою, і нам потрібно буде управляти цими даними, щоб передавати їх у нейронну мережу. Для цього потрібно зробити дві речі:

  1. Дати індекс кожному слову
  2. Створити матрицю для кожного тексту, де значення дорівнюють 1, якщо слово присутнє у тексті, і 0 в іншому випадку.

Розглянемо код, щоб зрозуміти цей процес:

import numpy as np    #numpy - це пакет для складних розрахунків
from collections import Counter
vocab = Counter()
text = "Hi from Brazil"
# Отримати всі слова
for word in text.split(' '):
    vocab[word]+=1
        
# Перетворити усі слова в індекси
def get_word_2_index(vocab):
    word2index = {}
    for i,word in enumerate(vocab):
        word2index[word] = i
        
    return word2index
# Тепер в нас є індекси
word2index = get_word_2_index(vocab)
total_words = len(vocab)
# Ось так ми створюємо масив у numpy (наша матриця)
matrix = np.zeros((total_words),dtype=float)
# Тепер ми заповнюємо матрицю значеннями
for word in text.split():
    matrix[word2index[word]] += 1
print(matrix)
>>> [ 1.  1.  1.]

У попередньому прикладі для тексту «Hi from Brazil» матриця мала вигляд [ 1. 1. 1.]. Що буде, якщо в тексті буде лише одне слово 'Hi'?

matrix = np.zeros((total_words),dtype=float)
text = "Hi"
for word in text.split():
    matrix[word2index[word.lower()]] += 1
print(matrix)
>>> [ 1.  0.  0.]

Потрібно зробити те ж саме з ярликами (категоріями), але тут буде використовуватись унітарне кодування:

y = np.zeros((3),dtype=float)
if category == 0:
    y[0] = 1.        # [ 1.  0.  0.]
elif category == 1:
    y[1] = 1.        # [ 0.  1.  0.]
else:
     y[2] = 1.       # [ 0.  0.  1.]

Запуск графу та отримання результатів

Ми підійшли до найцікавішої частини: вилучення результату з моделі. Давайте спочатку розглянемо більш детально вхідний набір даних.

Набір даних (Датасет)

Ми будемо використовувати 20 Newsgroups, датасет з 18 000 постами на 20 тем. Щоб завантажити його потрібно використати бібліотеку scikit-learn. Ми візьмемо звідти лише три категорії: comp.graphics, sci.space та rec.sport.baseball. Scikit-learn створює два підсети: один датасет для навчання, а другий для тестування. Рекомендація: ніколи не переглядайте тестові дані, оскільки це може вплинути на ваші дії під час створення моделі. Ви не повинні створювати модель, яка вже «знає», які значення їй потрібно прогнозувати.

Ось так ви будете завантажувати датасети:

from sklearn.datasets import fetch_20newsgroups
categories = ["comp.graphics","sci.space","rec.sport.baseball"]
newsgroups_train = fetch_20newsgroups(subset='train', categories=categories)
newsgroups_test = fetch_20newsgroups(subset='test', categories=categories)

Тренування моделі

У термінології нейронних мереж епоха (epoch) = одна ітерація у процесі тренування (отримання вихідних значень та оновлення ваг всіх тренувальних екземплярів).

Пам'ятайте метод tf.Session.run()? Розгляньмо його більш детально:

tf.Session.run(fetches, feed_dict=None, options=None, run_metadata=None)

У найпершому графі потоків даних з цієї статті ми використали операцію додавання, але ми також можемо передавати список операцій на запуск. У даній нейронній мережі ми передамо наступне: розрахунок похибки та оптимізація кроку.

Через параметр feed_dict ми передаємо дані для кожного запуску. Щоб передати ці дані нам також потрібно оголосити tf.placeholders (щоб заповнити feed_dict).

Це робиться так:

n_input = total_words # Слова у словнику
n_classes = 3         # Категорії: graphics, sci.space та baseball
input_tensor = tf.placeholder(tf.float32,[None, n_input],name="input")
output_tensor = tf.placeholder(tf.float32,[None, n_classes],name="output")

Тепер нам потрібно буде розділити тренувальні дані на партії.

Функція get_batches() повертає нам кільість текстів такого ж розміру, як і партія. Запустимо модель:

training_epochs = 10
# Запустити граф
with tf.Session() as sess:
    sess.run(init) # оголошує змінні (нормальний розподіл, пам'ятаєте?)
    # Цикл навчання
    for epoch in range(training_epochs):
        avg_cost = 0.
        total_batch = int(len(newsgroups_train.data)/batch_size)
        # Цикл через всі партії
        for i in range(total_batch):
            batch_x,batch_y = get_batch(newsgroups_train,i,batch_size)
            # Запустити операції оптимізації та розрахунку похибки
            c,_ = sess.run([loss,optimizer], feed_dict={input_tensor: batch_x, output_tensor:batch_y})

Тепер ваша модель навчена. Щоб протестувати її, вам потрібно створити елементи графу. Ми будемо вимірювати точність моделі, так що нам потрібно мати індекси прогнозованого значення та правильного значення (оскільки ми використовуємо унітарне кодування). Ми перевіряємо, чи отримані значення дорівнюють один одному.

    # Тестова модель
    index_prediction = tf.argmax(prediction, 1)
    index_correct = tf.argmax(output_tensor, 1)
    correct_prediction = tf.equal(index_prediction, index_correct)
    # Знаходження точності
    accuracy = tf.reduce_mean(tf.cast(correct_prediction, "float"))
    total_test_data = len(newsgroups_test.target)
    batch_x_test,batch_y_test = get_batch(newsgroups_test,0,total_test_data)
    print("Accuracy:", accuracy.eval({input_tensor: batch_x_test, output_tensor: batch_y_test}))
>>> Epoch: 0001 loss= 1133.908114347
    Epoch: 0002 loss= 329.093700409
    Epoch: 0003 loss= 111.876660109
    Epoch: 0004 loss= 72.552971845
    Epoch: 0005 loss= 16.673050320
    Epoch: 0006 loss= 16.481995190
    Epoch: 0007 loss= 4.848220565
    Epoch: 0008 loss= 0.759822878
    Epoch: 0009 loss= 0.000000000
    Epoch: 0010 loss= 0.079848485
    Optimization Finished!
    Accuracy: 0.75

І це все! Ви створили модель, використовуючи нейронну мережу, щоб класифікувати тексти у категорії. Вітаємо!

Помітили помилку? Повідомте автору, для цього достатньо виділити текст з помилкою та натиснути Ctrl+Enter
Codeguida 5.6K
Приєднався: 8 місяців тому
Коментарі (0)

    Ще немає коментарів

Щоб залишити коментар необхідно авторизуватися.

Вхід / Реєстрація