Tensorflow, Python, Visual Studio. Quick start

Автор: | 24.10.2019

Tags:  Tensorflow Python Visual Studio Quick start Линейная регрессия

Введение
Создание проекта и установка пакетов
Предсказание результата по выборке для обучения
Линейная регрессия
Полезные ссылки

Введение

TensorFlow — один из многих пакетов, используемых разработчиками для облегчения работы с машинным обучением и повышения его эффективности. Если раньше (при использовании библиотеки numpy) нам надо было создавать свои классы, чтобы хранить информацию о весах нейрона, объединять их в слои и еще добавлять туда алгоритмы обучения то теперь все это есть в TensorFlow.

Ниже рассмотрены возможности пакета TensorFlow на  на примерах задач линейной регрессии.

Создание проекта и установка пакетов

Если еще не установили поддержку Python в Visual Studio, то сделайте это (см. Инсталляция Python в Visual Studio).

Запустите Visual Studio и создайте проект приложения (File>New>Project>Python>Python Application).

Для тестирования системы вставьте код в файл с расширением .py и запустите приложение:

# Import tensorflow`
import tensorflow as tf

# Initialize two constants
x1 = tf.constant([1,2,3,4])
x2 = tf.constant([5,6,7,8])

# Multiply
result = tf.multiply(x1, x2)

# Print the result
print(result)

Если появляются ошибки, то система подскажет,  какой пакет еще не установлен. Для запуска программ используется пакет tensorflow (версия 2.6.0) . Пакеты добавляются непосредственно из проекта (см. Создание нейронной сети в Visual Studio).

Предсказание результата по выборке для обучения

В этом разделе рассмотрена программа, в которой предсказывается результат по выборке входных и выходных данных для обучения :

Значения на входе связаны со значениями на выходе выражением:

F = 1.8* C + 32

где С — градусы Цельсия (входные значения), F — Фаренгейта (выходные значения).

Но мы находим решение не с помощью известного уравнения, а тренируем нейронную сеть через повторяющиеся попытки сопоставить входные значения выходным.

Ниже приводится код к этой программе. Подробное описание кода и алгоритма к нему см. в первоисточнике.

from __future__ import absolute_import, division, print_function, unicode_literals
import tensorflow as tf
import logging
logging.getLogger("tensorflow").setLevel(logging.ERROR)
import numpy as np
import matplotlib.pyplot as plt

celsius_q = np.array([0, 8, 15, 22], dtype=float)
fahrenheit_a = np.array([32, 46, 59, 72], dtype=float)

for i,c in enumerate(celsius_q):
 print("{} градусов Цельсия = {} градусов Фаренгейта".format(c, fahrenheit_a[i]))

l0 = tf.keras.layers.Dense(units=4, input_shape=[1])
l1 = tf.keras.layers.Dense(units=4)
l2 = tf.keras.layers.Dense(units=1)
model = tf.keras.Sequential([l0, l1, l2])

model.compile(loss='mean_squared_error',
 optimizer=tf.keras.optimizers.Adam(0.1))
history = model.fit(celsius_q, fahrenheit_a, epochs=500, verbose=False)
print("Завершили тренировку модели:")
plt.xlabel('Epoch')
plt.ylabel('Loss')
plt.plot(history.history['loss'])
plt.show()

print(model.predict([38.0]))

На рисунках ниже отображен результат работы программы — график изменения функции потерь и предсказанное число.

Число на выходе (100. 9…) было получено по числу на входе (38).

Расхождение с точным результатом (F = 1.8* 38 + 32  =100.4) составило около 0.5%.

Задание: Перестройте программу, чтобы она решала следующую задачу:

Если раньше (при использовании библиотеки numpy) нам надо было создавать свои классы, чтобы хранить информацию о весах нейрона, объединять их в слои и еще добавлять туда алгоритмы обучения то теперь все это есть в TensorFlow.Для сравнения смотри решение аналогичной задачи на основе библиотеки numpy

Решение: В предыдущей задаче весь датасет был разбит на 4 батча, в каждом из которых было по 1 числу на входе и выходе. Для реализации задачи была использована модель с одним входным и выходным нейронами. Через такую нейронную сеть за одну эпоху пропускается  датасет  через 4 итерации. Подробнее см. Эпоха, батч, итерация — в чем различия?

В этой задаче весь датасет разбиваем на 3 батча по 2 числа на входе и выходе. Для реализации задачи соответственно используется модель с 2-я нейронами на входе и выходе.

Через нейронную сеть  за одну эпоху пропускается весь датасет через 3 итерации.

from __future__ import absolute_import, division, print_function, unicode_literals
import tensorflow as tf
import logging
logging.getLogger("tensorflow").setLevel(logging.ERROR)
import numpy as np
import matplotlib.pyplot as plt

X = np.array([[1, 2], [3, 4],[5, 6]], dtype=float)
y= np.array([[2, 3], [4, 5], [6, 7]], dtype=float)

l0 = tf.keras.layers.Dense(units=4, input_shape=[2])
l1 = tf.keras.layers.Dense(units=4)
l2 = tf.keras.layers.Dense(units=2)
model = tf.keras.Sequential([l0, l1, l2])

model.compile(loss='mean_squared_error',
 optimizer=tf.keras.optimizers.Adam(0.1))
history = model.fit(X, y, epochs=500, verbose=False)
print("Завершили тренировку модели:")
print(y)
plt.xlabel('Epoch')
plt.ylabel('Loss')
plt.plot(history.history['loss'])
plt.show()

print(model.predict([[4, 5]]))

Попробуем пропустить весь датасет через 1 итерацию (используем 1 батч). В этом случае модель НС будет иметь вместо 6 входов и выходов:

from __future__ import absolute_import, division, print_function, unicode_literals
import tensorflow as tf
import logging
logging.getLogger("tensorflow").setLevel(logging.ERROR)
import numpy as np
import matplotlib.pyplot as plt

X = np.array([[1, 2, 3, 4, 5, 6]], dtype=float)
y= np.array([[2, 3, 4, 5, 6, 7]], dtype=float)

l0 = tf.keras.layers.Dense(units=2, input_shape=[6])
l1 = tf.keras.layers.Dense(units=2)
l2 = tf.keras.layers.Dense(units=6)
model = tf.keras.Sequential([l0,l1,l2])

model.compile(loss='mean_squared_error',
 optimizer=tf.keras.optimizers.Adam(0.1))
history = model.fit(X, y, epochs=1500, verbose=False)
print("Завершили тренировку модели:")
plt.xlabel('Epoch')
plt.ylabel('Loss')
plt.plot(history.history['loss'])
plt.show()

print(model.predict([[1, 2, 3, 4, 5, 6]]))

Такая модель (с 6 входами и лишь одним набором чисел) обучена только на этот конкретный набор чисел. Если изменим на входе при тестировании какое-то из чисел набора, например: print(model.predict( [[1, 2, 3, 5, 5, 6]] )), то это не только даст точного результата для этого числа из набора (5 вместо ожидаемых 6), но также повлияет и на точность результата  других чисел из набора:

Результат может быть улучшен, если мы более наглядно дадим понять модели НС, что мы от нее хотим. Для этого увеличим выборку для обучения:

X = np.array([[1, 2, 3, 4, 5, 6], [2, 3, 4, 5, 6, 7], [3, 4, 5, 6, 7, 8]], dtype=float)
y= np.array([[2, 3, 4, 5, 6, 7],[3, 4, 5, 6, 7, 8], [4, 5, 6, 7, 8, 9]], dtype=float)

Результат немного улучшился для тестируемого нами 4-го числа (5.2985… ближе к 6, чем 5.085…), однако ухудшился для других чисел в тестируемом наборе [1, 2, 3, 4, 5, 6].

Разместим числа в обучающей выборке непоследовательно:

X = np.array([[3, 2, 1, 7, 6, 5], [7, 3, 4, 5, 6, 3], [3, 5, 1, 6, 7, 4]], dtype=float)
y= np.array([[4, 3, 2, 8, 7, 6],[8, 4, 5, 6, 7, 4], [4, 6, 2, 7, 8, 5]], dtype=float)

Совсем запутали нейронную сеть (((. Для сети непонятно, что мы от нее хотим.

Вернемся к исходной модели с одним входом и выходом (как в задачке про градусы), при этом оставим числа для обучения и тестирования, используемые в последней модели.

X = np.array([3, 2, 1, 7, 6, 5, 7, 3, 4, 5, 6, 3, 3, 5, 1, 6, 7, 4], dtype=float)
y= np.array([4, 3, 2, 8, 7, 6, 8, 4, 5, 6, 7, 4, 4, 6, 2, 7, 8, 5], dtype=float)

l0 = tf.keras.layers.Dense(units=2, input_shape=[1])
l1 = tf.keras.layers.Dense(units=2)
l2 = tf.keras.layers.Dense(units=1)
model = tf.keras.Sequential([l0,l1,l2])
...
print(model.predict([1, 2, 3, 5, 5, 6]))

Результат соответствует ожидаемому — каждое из тестируемых чисел увеличилось на 1.

Выводы: В экспериментах всего лишь по разному разбивали dataset на batches. Однако, при этом получали разные результаты. Это объясняется тем, что после разбивки datasets на batches по сути получали разные постановки для реализации задач:

  1. Как 1 число влияет на другое число
  2. Как пара чисел влияет на другую пару чисел
  3. Как одно множество чисел влияет на другое множество чисел

Правильно выбирайте модель НС и datasets для ее обучения. Для этого нужно хотя бы интуитивно почувствовать, что и как может повлиять на конечный результат. Затем нужно проверить свои предположения на тестовых примерах.

Линейная регрессия

На рисунке отображен результат работы программы, в которой реализована линейная регрессия  с использованием автоматической генерации градиента в TensorFlow 2.0.

Ниже приводится код к этой программе. Подробное описание кода см. в первоисточнике.

import tensorflow as tf
import matplotlib.pyplot as plt
import numpy as np

# number of training examples
n_examples = 1000 
# number of steps we are going to train for
training_steps = 1000 
# after multiples of this display the loss
display_step = 10 
# multipliying factor on gradient
learning_rate = 0.001 
# gradient and y intercept of our line, edit these for a different linear problem
m, c = 6, -5

# A dataset of points around mx + c
def train_data(n, m, c):
 x = tf.random.normal([n]) # n values taken from a normal distribution, mean = 0, SD =1
 noise = tf.random.normal([n])# n values taken from a normal distribution, mean = 0, SD =1
 y = m*x + c + noise # our scatter plot
 return x, y

# Start with random values for W and B on the same batch of data
x, y = train_data(n_examples,m,c) # our training values x and y
plt.scatter(x,y)
plt.xlabel("x")
plt.ylabel("y")
plt.title("Figure 1: Training Data")

def prediction(x, weight, bias):
 # our predicted (learned) m and c, expression is like y=m*x + c
 return weight*x + bias
 
 
# A loss function using mean-squared error
def loss(x, y, weights, biases):
 # how 'wrong' our predicted (learned) y is
 error = prediction(x, weights, biases) - y 
 
 squared_error = tf.square(error)
 
 # overall mean of squared error
 return tf.reduce_mean(input_tensor=squared_error)
 
 
# Find the derivative of loss with respect to weight and bias
def grad(x, y, weights, biases):
 # magical feature of tensorflow to calculate gradient for backpropagation
 with tf.GradientTape() as tape:
  loss_ = loss(x, y, weights, biases)
 
 # direction and value of the gradient of our loss w.r.t weight and bias
 return tape.gradient(loss_, [weights, biases])

W = tf.Variable(np.random.randn()) # initial, random, value for predicted weight (m)
B = tf.Variable(np.random.randn()) # initial, random, value for predicted bias (c)
print("Initial loss: {:.3f}".format(loss(x, y, W, B)))

for step in range(training_steps): #iterate for each training step
 # direction (sign) and value of the gradient of our loss w.r.t weight and bias
 deltaW, deltaB = grad(x, y, W, B) 
 
 change_W = deltaW * learning_rate # adjustment amount for weight
 change_B = deltaB * learning_rate # adjustment amount for bias
 
 W.assign_sub(change_W) # subract change_W from W
 B.assign_sub(change_B) # subract change_B from B
 
 if step==0 or step % display_step == 0:
 # print(deltaW.numpy(), deltaB.numpy()) # uncomment if you want to see the gradients
  print("Loss at step {:02d}: {:.6f}".format(step, loss(x, y, W, B)))

print("Final loss: {:.3f}".format(loss(x, y, W, B)))
print("W = {}, B = {}".format(W.numpy(), B.numpy()))
print("Compared with m = {:.1f}, c = {:.1f}".format(m, c)," of the original line")
xs = np.linspace(-3, 4, 50)
ys = W.numpy()*xs + B.numpy()
plt.scatter(xs,ys)
plt.xlabel("x")
plt.ylabel("y")
plt.title("Figure 2: Line of Best Fit")
plt.show()

 

Полезные ссылки:

 

Автор: Николай Свирневский