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


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

Розповідає Дебра Мескіта


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

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

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

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

  1. Як працює TensorFlow?
  2. Що таке машинно-навчені моделі?
  3. Що таке нейронна мережа?
  4. Як нейронна мережа навчається?
  5. Як управляти даними і передавати їх на введення нейронної мережі?
  6. Як запускати модель і отримувати результати прогнозування?

Під час прочитання нашої статті вам може знадобитися керівництво по використанню TensorFlow, тримайте вкладку з ним відкритою.

TensorFlow

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

tf.Graph

Кожне обчислення в TensorFlow представляється як граф потоку даних. У нього є два елементи:

  1. Набір tf.Operation, який представляє одиниці обчислень.
  2. Набір tf.Tensor, який представляє одиниці даних.

Щоб побачити, як це усе працює, створимо наступний граф потоку даних :

Граф, що виконує x+y

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

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

Тепер визначимо одиницю операції :

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

У нас є усі елементи графа. Пора його побудувати:

import tensorflow as tfmy_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 (принаймні, так сказано в документації). Щоб зробити це, необхідно визначити, який граф ми використовуватимемо в сесії:

import tensorflow as tfmy_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, запускаючи необхідний фрагмент графа для виконання кожного об’єкту Operation і оцінки кожного Tensor, переданого в аргументі fetches. У нашому випадку запускається крок операції складання:

import tensorflow as tfmy_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 працює, потрібно створити прогнозуючу модель. Коротко:

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

Процес побудови такий:

Процес створення прогнозуючої моделі

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

Робочий процес прогнозування

Мета моделі, яку ми створимо, полягатиме в класифікації тексту за категоріями, їх можна задати:

  • введення: текст;
  • результат: категорія.

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

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

Для створення моделі використовуємо нейронні мережі.

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

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

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

Нейронна мережа

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

Щоб зрозуміти, як працюють нейронні мережі, побудуємо архітектуру однієї з них за допомогою TensowFlow. Можете поглянути на приклад такій реалізації.

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

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

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

Шар введення і перший прихований шар

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

У шарі введення один вузол відповідає слову з набору даних. Розглянемо це трохи пізніше.

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

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

Існує багато видів функцій активації. Використовуємо усічене лінійне перетворення (ReLu). Ця функція визначається таким чином:

f (x) = max (0, x) [висновок рівний x або 0 (нуль), залежно від того, що більше]

Приклади: якщо x = − 1, то f (x) = 0 (нуль); якщо x = 0,7, то f (x) = 0,7.

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

Другий прихований шар робить те ж саме, що і перший, але тепер вхідними даними є вихідні дані першого шару:

Перший і другий приховані шари

Шар виведення

І, нарешті, ми добираємося до останнього пункту – шару виведення. Щоб отримати його результати, скористаємося унітарним кодуванням. Тут тільки один біт дорівнює одиниці, а усі інші – нульові. Наприклад, ми хочемо закодувати три категорії: “спорт”, “космос” і “комп’ютерна графіка”:

+-------------------+-----------+| категорія | значення |+-------------------|-----------+| спорт | 001 || космос | 010 || комп. графіка | 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]))}

Запустимо мережу вперше з значеннями ваги, отриманими по нормальному розподілу :

input values: xweights: wbias: boutput values: zexpected values: expected

Щоб дізнатися, чи вчиться мережа, необхідно порівняти вихідні значення z з очікуваними значеннями expected. Існує багато методів, як порахувати втрату loss. Оскільки ми працюємо із завданням на класифікацію, кращим способом обчислення помилки буде перехресна ентропія.

Зробимо це за допомогою TensorFlow, використовуючи метод tf.nn.softmax_cross_entropy_with_logits () (функцію активації softmax) і вичислимо середню помилку tf.reduced_mean ():

# Конструювання моделі
prediction = multilayer_perceptron (input_tensor, weights, biases) # Визначення потер'є ntropy_loss = tf.nn.softmax_cross_entropy_with_logits (logits=prediction, labels=output_tensor) loss = tf.reduce_mean (entropy_loss)

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

Градієнтний спуск

Існує безліч алгоритмів для вичислення градієнтного спуску, ми будем використовувати адаптивну оцінку моментів. Передадмо значення learning_rate, котре визначить крок значень для нахдження кращої ваги.

Метод tf.train.AdamOptimizer (learning_rate).minimize (loss) – це синтаксичний цукор, який робить дві речі:

  1. compute_gradients (loss, ).
  2. apply_gradients ().

Оновлюючи значення усіх tf.Variables, нам не доводиться передавати список змінних. І тепер є код для тренування мережі :

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. Створити матрицю для кожного тексту, де значення дорівнює одиниці, якщо слово є в тексті, і нулю в іншому разі.

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

import numpy as np #numpy - це пакет для наукових розрахунківfrom collections import Countervocab = 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]] += 1print (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 ()]] += 1print (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 тисяч постів на 20 тем. Щоб завантажити такий об’єм інформації, скористаємося бібліотекою scikit – learn. Візьмемо тільки 3 категорії: comp.graphics, sci.space і rec.sport.baseball. Scikit – learn працює з двома підмножинами: однією для навчання, другою для перевірки.

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

Ось так ви завантажите свої набори даних :

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

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

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

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

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

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

Параметр feed_dict вказує, куди ми передаємо дані по кожному етапу роботи. Також необхідно визначити tf.placeholders, щоб наповнити feed_dict.

Як свідчить документація TensorFlow:

Плейсхолдер потрібний виключно в якості мети наповнення. Він не ініціалізував і не містить даних.

Тому ми визначимо заповнювачі так:

n_input = total_words # Слова в словареn_classes = 3 # Категоріїinput_tensor = tf.placeholder (tf.float32,[None, n_input],name=" input") output_tensor = tf.placeholder (tf.float32,[None, n_classes],name=" output")

Розіб’ємо тренувальні дані на блоки згідно офіційному сайту:

Якщо ви використовуєте заповнювач для введення, можна визначати змінний вимір блоку з допомогою tf.placeholder (..., shape=[None,...]). Елемент None вказує на вимір зі змінним розміром.

Для тестування моделі ми наповнимо словник великим блоком, тому необхідно визначити його змінний вимір.

Функція 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

От і все! Ми створили модель з використанням нейронної мережі для класифікації текстів за категоріями. Вітаю!

Кінцевий код ви можете подивитися на сторінці GitHub.

Примітка: Змініть значення, які ми визначили, щоб подивитися, як вони впливають на тренувальний час і точність моделі.

Переклад статті “Big Picture Machine Learning: Classifying Text with Neural Networks and TensorFlow”

Київ, Харків, Одеса, Дніпро, Запоріжжя, Кривий Ріг, Вінниця, Херсон, Черкаси, Житомир, Хмельницький, Чернівці, Рівне, Івано-Франківськ, Кременчук, Тернопіль, Луцьк, Ужгород, Кам'янець-Подільський, Стрий - за статистикою саме з цих міст програмісти найбільше переїжджають працювати до Львова. А Ви розглядаєте relocate?


Залишити відповідь

Ваша e-mail адреса не оприлюднюватиметься. Обов’язкові поля позначені *