Pull to refresh

Какие закономерности находят нейросети?

Reading time6 min
Views8.6K
В этом посте я хочу рассказать о закономерностях, которые могут находить нейросети. Во многих руководствах для начинающих акцент делается на технике написания кода для нейронных сетей, при этом вопросы «логики» (что могут нейросети? какие архитектуры лучше подходят для каких задач и почему?) часто остаются в стороне. Я надеюсь, мой пост поможет начинающим лучше понять возможности нейронных сетей. Для этого мы попробуем посмотреть, как они справляются с некоторыми модельными задачами. Примеры кода будут приводиться на python с использованием библиотеки keras.

Задача 1. Начнём с простого. Построим нейронную сеть, аппроксимирующую синус.

import numpy as np
import matplotlib
import matplotlib.pyplot as plt
from keras.models import Sequential
from keras.layers import Dense


def get_X_y(n):
    X = np.random.uniform(0, np.pi, n)
    y = np.sin(X)
    return X, y


n = 40
X, y = get_X_y(n)
print("X shape:", X.shape)

model = Sequential()
model.add(Dense(6, input_dim=1, activation='relu'))
model.add(Dense(4, activation='relu'))
model.add(Dense(1, activation='sigmoid'))

model.compile(loss='mean_squared_error', optimizer='adam', metrics=['mean_squared_error'])

model.fit(X, y, epochs=1000, batch_size=4)

X_test = np.linspace(start=0, stop=np.pi, num=500)
print("X test shape:", X_test.shape)
y_test = model.predict(X_test)

font = {'weight': 'bold',
        'size': 25}

matplotlib.rc('font', **font)
axes = plt.gca()
axes.set_ylim(0, 1)
plt.plot(X_test, y_test, c='green', marker='o', markersize=5)
plt.title("Sinus approximated by neural network")
plt.yticks(np.arange(0, 1, 0.1))
plt.grid()
plt.show()

Получаем следующий график:



Как видим, нейронная сеть успешно справилась с задачей аппроксимации несложной функции.

Задача 2. Посмотрим, как нейросеть справится с более сложной задачей. Будем подавать на вход значения x, равномерно распределённые на отрезке [0, 1], а y будем задавать случайно: при x < 0.6 y будет случайной величиной, принимающей значение 0 с вероятностью 0.75 и 1 с вероятностью 0.25 (то есть биномиальной случайной величиной с p=0.25). При x > 0.6 y будет случайной величиной, принимающей значение 0 с вероятностью 0.3 и значение 1 с вероятностью 0.7. В качестве оптимизируемой функции возьмем среднеквадратическую ошибку.

import numpy as np
import matplotlib
import matplotlib.pyplot as plt
from keras.models import Sequential
from keras.layers import Dense


def get_X_y(n):
    X = np.random.uniform(0, 1, n)
    y0 = np.random.binomial(size=n, n=1, p=0.25)
    y1 = np.random.binomial(size=n, n=1, p=0.7)
    y = np.where(X < 0.6, y0, y1)
    return X, y


n_inputs = 1
n_hidden1 = 100
n_hidden2 = 50
n_outputs = 1

n = 2000

X, y = get_X_y(n)
print("X shape:", X.shape)

model = Sequential()
model.add(Dense(n_hidden1, input_dim=1, activation='relu'))
model.add(Dense(n_hidden2, activation='relu'))
model.add(Dense(1, activation='sigmoid'))

model.compile(loss='mean_squared_error', optimizer='adam', metrics=['accuracy'])

model.fit(X, y, epochs=200, batch_size=100)

X_test = np.linspace(start=0, stop=1, num=100)
print("X test shape:", X_test.shape)
y_test = model.predict(X_test)

font = {'weight': 'bold',
        'size': 25}

matplotlib.rc('font', **font)
axes = plt.gca()
axes.set_ylim(0, 1)
plt.plot(X_test, y_test, c='green', marker='o', markersize=5)
plt.title("Binomial distribution approximated by neural network")
plt.yticks(np.arange(0, 1, 0.1))
plt.grid()
plt.show()

Получаем следующий график аппроксимированной нейросетью функции:



Как видим, нейронная сеть аппроксимировала математическое ожидание нашей случайной величины y. Итак, нейросети могут (в принципе) приближать средние значения случайных величин, зависящих от параметров. Например, мы можем ожидать от них решения следующей задачи: люди с доходом до 1000 долларов в среднем несчастны, а люди с доходом выше 1000 долларов в среднем счастливы; надо научиться предсказывать «уровень счастья» в зависимости от дохода. Нейронная сеть сможет найти зависимость среднего уровня счастья от дохода, несмотря на то, что среди людей с любым уровнем дохода встречаются как счастливые, так и несчастные.

Задача 3. Теперь перейдём к предсказанию последовательностей. Будем рассматривать последовательности из 0 и 1, заданные следующим правилом: 10 членов — равновероятно 0 или 1, а одиннадцатый равен 1, если предыдущий член 0, и равновероятно 0 или 1, если предыдущий член 1. Будем генерировать такие последовательности длины 11 (10 входных членов последовательности и один, последний, мы предсказываем) и на них обучать нашу рекуррентную нейронную сеть. А после обучения проверим, как она справляется с предсказанием на новых последовательностях (также длины 11).

import numpy as np
from keras.models import Sequential
from keras.layers import LSTM, Dense


def get_X_y(m, n):
    X = np.random.binomial(size=(m,n), n=1, p=0.5)
    y0 = np.ones(m)
    y1 = np.random.binomial(size=m, n=1, p=0.5)
    y = np.where(X[:, n-1]==0, y0, y1)
    X = np.reshape(X, (X.shape[0], X.shape[1], 1))
    return X, y


model = Sequential()
model.add(LSTM(units=50))
model.add(Dense(units=1))
model.compile(optimizer = 'adam', loss = 'mean_squared_error')

X_train, y_train = get_X_y(1000, 10)
model.fit(X_train, y_train, epochs = 20, batch_size = 32)

m_test = 12
n_test = 10
X_test, y_test = get_X_y(m_test, n_test)
y_predicted = model.predict(X_test)

for i in range(m_test):
    print("x_last=",  X_test[i, n_test-1, 0], "y_predicted=", y_predicted[i, 0])

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

Номер последовательности Предпоследний член последовательности Предсказанное значение
1 0 0.96
2 0 0.95
3 0 0.97
4 0 0.96
5 0 0.96
6 1 0.45
7 0 0.94
8 1 0.50
9 0 0.96
10 1 0.42
11 1 0.44
12 0 0.92


Как видим, если предпоследний член последовательности 0, то нейронная сеть предсказывет близкое к 1 значение, а если он равен 1, то близкое к 0.5 значение. Это близко к оптимальному прогнозу. Аналогичный пример «из жизни» мог бы выглядеть так: «если я сегодня иду в кино, то завтра я обедаю в ресторане; если я сегодня иду в театр, то завтра обедаю где попало». Нейросеть, как мы убедились, может отлавливать закономерности такого типа и предсказать поход в ресторан по походу в кино (а по походу в театр предсказать «нечто среднее»).

Задача 4. Усложним задачу нейросети. Пусть всё будет как в предыдущем примере, только одиннадцатый член последовательности будет определяться не предыдущим, а вторым членом последовательности (по тому же правилу). Код здесь приводить не будем, поскольку он практически не отличается от предыдущего. Мой эксперимент показал, что нейросеть всё равно находит закономерность, но за больше время (пришлось использовать для обучения 100 эпох вместо 20).
Таким образом, нейронные сети могут (опять уточним — в принципе) отлавливать достаточно долгосрочные зависимости (в нашем «жизненном примере» — отлавливать закономерности типа «я иду в ресторан сегодня, если неделю назад я был в кино»).

Задача 5. Посмотрим, как нейросеть использует имеющуюся информацию для прогноза.
Для этого проведём обучение на последовательностях длины 4. Всего у нас будет 3 разных равновероятных последовательности:

0, 0, 1, 1
0, 1, 0, 1
0, 1, 1, 0


Таким образом, после начальной комбинации 0, 0 мы всегда встретим две единицы, после комбинации 0, 1 мы равновероятно встретим 0 или 1, зато последнее число мы будем знать наверняка. Нашу нейросеть мы теперь попросим возвращать последовательности, поставив return_sequences=True. В качестве прогнозируемых последовательностей возьмем наши же последовательности, сдвинутые на один шаг и дополненные справа нулём. Теперь мы уже можем предположить, что получится: на первом шаге нейросеть будет выдавать число, близкое к 2/3 (поскольку с вероятностью 2/3 второй член равен 1), а дальше для комбинации 0, 0 она будет выдавать два числа, близких к единице, а для 0, 1 сначала выдаст число, близкое к 0.5, а затем выдаст число, близкое к 0 или 1, в зависимости от того, получили ли мы последовательность 0, 1, 0 или 0, 1, 1. В конце нейросеть всегда будет выдавать число, близкое к 0. Проверка с помощью следующего кода показывает, что наши предположения верны.

import numpy as np
from keras.models import Sequential
from keras.layers import LSTM, Dense
import random


def get_X_y(n):
    X = np.zeros((n, 4))
    z = np.array([random.randint(0, 2) for i in range(n)])
    X[z == 0, :] = [0, 0, 1, 1]
    X[z == 1, :] = [0, 1, 0, 1]
    X[z == 2, :] = [0, 1, 1, 0]
    y = np.zeros((n, 4))
    y[:, :3] = X[:, 1:]
    X = np.reshape(X, (X.shape[0], X.shape[1], 1))
    y = np.reshape(y, (y.shape[0], y.shape[1], 1))
    return X, y


model = Sequential()
model.add(LSTM(units=20, return_sequences=True))
model.add(Dense(units=1))
model.compile(optimizer = 'adam', loss = 'mean_squared_error')

X_train, y_train = get_X_y(1000)
model.fit(X_train, y_train, epochs = 100, batch_size = 32)

X_test = np.zeros((3, 4))
X_test[0, :] = [0, 0, 1, 1]
X_test[1, :] = [0, 1, 0, 1]
X_test[2, :] = [0, 1, 1, 0]
X_test = np.reshape(X_test, (3, 4, 1))

y_predicted = model.predict(X_test)
print(y_predicted)


Из этого примера мы видим, что нейросеть может динамически менять прогноз в зависимости от поступившей информации. Так же поступали бы и мы, пробуя спрогнозировать некую последовательность: когда имеющаяся информация позволяет оценить вероятности исходов на следующем шаге, мы прогнозируем, исходя из этой информации; но когда мы узнаём дополнительную информацию на следующем шаге, то в зависимости от неё меняем прогноз.
Так, если мы видим, что кто-то идёт к нам из темноты, то говорим «это человек, детальнее мы не знаем»; когда мы в темноте начинаем различать длинные волосы, то говорим «это, наверное, женщина». Но если вслед за тем мы рассмотрим, что у человека есть усы, то скажем, что это, наверняка, мужчина (хоть и с длинными волосами). Подобно этому поступает, как мы видели, и нейросеть, используя всю полноту имеющейся на данный момент информации для прогноза.

Итак, мы посмотрели на простых примерах, как работают нейронные сети и какие закономерности они могут находить. В целом, мы увидели, что часто нейросети ведут себя вполне «разумно», делая прогнозы, близкие к тем, которые бы сделал человек. Хотя, надо заметить, для того, чтобы уловить простые закономерности, им требуется гораздо больше данных, чем людям.
Tags:
Hubs:
Total votes 38: ↑17 and ↓21-4
Comments7

Articles