Seminar4-ru-mnist


Theano, Lasagne

и с чем их едят

разминка

  • напиши на numpy функцию, которая считает сумму квадратов чисел от 0 до N, где N - аргумент
  • массив чисел от 0 до N - numpy.arange(N)

In [ ]:
!pip install install Theano==0.8.2
!pip install https://github.com/Lasagne/Lasagne/archive/master.zip

In [1]:
import numpy as np
def sum_squares(N):
    return сумма квадратов чисел от 0 до N

In [2]:
%%time
sum_squares(10**8)


CPU times: user 760 ms, sys: 11 s, total: 11.7 s
Wall time: 13.8 s
Out[2]:
662921401752298880

theano teaser

Как сделать то же самое


In [3]:
import theano
import theano.tensor as T

In [4]:
#будущий параметр функции
N = T.scalar("a dimension",dtype='int32')


#рецепт получения суммы квадратов
result = (T.arange(N)**2).sum()

#компиляция функции "сумма квадратов" чисел от 0 до N
sum_function = theano.function(inputs = [N],outputs=result)

In [5]:
%%time
sum_function(10**8)


CPU times: user 777 ms, sys: 6.71 s, total: 7.49 s
Wall time: 7.52 s
Out[5]:
array(662921401752298880)

Как оно работает?

  • Нужно написать "рецепт" получения выходов по входам
  • То же самое на заумном: нужно описать символический граф вычислений
  • 2 вида зверей - "входы" и "преобразования"
  • Оба могут быть числами, массивами, матрицами, тензорами и т.п.
  • Вход - это то аргумент функции. То место, на которое подставится аргумент вызове.
    • N - вход в примере выше
  • Преобразования - рецепты вычисления чего-то на основе входов и констант
    • (T.arange(N)^2).sum() - 3 последовательных преобразования N
    • Работают почти 1 в 1 как векторные операции в numpy
    • почти всё, что есть в numpy есть в theano tensor и называется так же
      • np.mean -> T.mean
      • np.arange -> T.arange
      • np.cumsum -> T.cumsum
      • и так далее...
      • Совсем редко - бывает, что меняется название или синтаксис - нужно спросить у семинаристов или гугла

Ничего не понятно? Сейчас исправим.


In [6]:
#входы
example_input_integer = T.scalar("вход - одно число(пример)",dtype='float32')

example_input_tensor = T.tensor4("вход - четырёхмерный тензор(пример)")
#не бойся, тензор нам не пригодится



input_vector = T.vector("вход - вектор целых чисел", dtype='int32')

In [7]:
#преобразования

#поэлементное умножение
double_the_vector = input_vector*2

#поэлементный косинус
elementwise_cosine = T.cos(input_vector)

#разность квадрата каждого элемента и самого элемента
vector_squares = input_vector**2 - input_vector

In [8]:
double_the_vector


Out[8]:
Elemwise{mul,no_inplace}.0

In [ ]:
#теперь сам:
#создай 2 вектора из чисел float32
my_vector = <вектор из float32>
my_vector2 = <ещё один такой же>

In [ ]:
#напиши преобразование, которое считает
#(вектор 1)*(вектор 2) / (sin(вектор 1) +1)
my_transformation = <преобразование>

In [ ]:
print (my_transformation)
#то, что получилась не чиселка - это нормально

Компиляция

  • До этого момента, мы использовали "символические" переменные
    • писали рецепт вычислений, но ничего не вычисляли
  • чтобы рецепт можно было использовать, его нужно скомпилировать

In [ ]:
inputs = [<от чего завсит функция>]
outputs = [<что вычисляет функция (можно сразу несколько - списком, либо 1 преобразование)>]

# можно скомпилировать написанные нами преобразования как функцию
my_function = theano.function(
    inputs,outputs,
    allow_input_downcast=True #автоматически прводить типы (необязательно)
 )

In [ ]:
#можно вызвать вот-так:
print ("using python lists:")
print (my_function([1,2,3],[4,5,6]))
print()

#а можно так. 
#К слову, ту тип float приводится к типу второго вектора
print ("using numpy arrays:")
print (my_function(np.arange(10),
                  np.linspace(5,6,10,dtype='float')))

хинт для отладки

  • Если ваша функция большая, компиляция может отнять какое-то время.
  • Чтобы не ждать, можно посчитать выражение без компиляции
  • Вы экономите время 1 раз на компиляции, но сам код выполняется медленнее

In [ ]:
#словарик значений для входов
my_function_inputs = {
    my_vector:[1,2,3],
    my_vector2:[4,5,6]
}

#вычислить без компиляции
#если мы ничего не перепутали, 
#должно получиться точно то же, что и раньше
print my_transformation.eval(my_function_inputs)


#можно вычислять преобразования на ходу
print ("сумма 2 векторов", (my_vector + my_vector2).eval(my_function_inputs))

#!ВАЖНО! если преобразование зависит только от части переменных,
#остальные давать не надо
print ("форма первого вектора", my_vector.shape.eval({
        my_vector:[1,2,3]
    }))
  • Для отладки желательно уменьшить масштаб задачи. Если вы планировали послать на вход вектор из 10^9 примеров, пошлите 10~100.
  • Если #ОЧЕНЬ нужно послать большой вектор, быстрее скомпилировать функцию обычным способом

Теперь сам: MSE (2 pts)


In [ ]:
# Задание 1 - напиши и скомпилируй theano-функцию, которая считает среднеквадратичную ошибку двух векторов-входов
# Вернуть нужно одно число - собственно, ошибку. Обновлять ничего не нужно

<твой код - входы и преобразования>

compute_mse =<твой код - компиляция функции>

In [ ]:
#тесты
from sklearn.metrics import mean_squared_error

for n in [1,5,10,10**3]:
    
    elems = [np.arange(n),np.arange(n,0,-1), np.zeros(n),
             np.ones(n),np.random.random(n),np.random.randint(100,size=n)]
    
    for el in elems:
        for el_2 in elems:
            true_mse = np.array(mean_squared_error(el,el_2))
            my_mse = compute_mse(el,el_2)
            if not np.allclose(true_mse,my_mse):
                print ('Wrong result:')
                print ('mse(%s,%s)'%(el,el_2))
                print ("should be: %f, but your function returned %f"%(true_mse,my_mse))
                raise ValueError("Что-то не так")

print ("All tests passed")

Shared variables

  • Входы и преобразования - части рецепта.
    • Они существуют только во время вызова функции.
  • Shared переменные - всегда остаются в памяти
    • им можно поменять значение
      • (но не внутри символического графа. Об этом позже)
    • их можно включить в граф вычислений
  • хинт - в таких переменных удобно хранить параметры и гиперпараметры
    • например, веса нейронки или learning rate, если вы его меняете

In [9]:
#cоздадим расшаренную перменную
shared_vector_1 = theano.shared(np.ones(10,dtype='float64'))

In [10]:
#получить (численное) значение переменной
print ("initial value",shared_vector_1.get_value())


initial value [ 1.  1.  1.  1.  1.  1.  1.  1.  1.  1.]

In [11]:
#задать новое значение
shared_vector_1.set_value( np.arange(5) )

#проверим значение
print ("new value", shared_vector_1.get_value())

#Заметь, что раньше это был вектор из 10 элементов, а сейчас - из 5. 
#Если граф при этом остался выполним, это сработает.


new value [ 0.  1.  2.  3.  4.]

Теперь сам


In [ ]:
#напиши рецепт (преобразование), которое считает произведение(поэллементное) shared_vector на input_scalar
#скомпилируй это в функцию от input_scalar

input_scalar = T.scalar('coefficient',dtype='float32')

scalar_times_shared = <рецепт тут>


shared_times_n = <твой код, который компилирует функцию>

In [ ]:
print ("shared:", shared_vector_1.get_value())

print ("shared_times_n(5)",shared_times_n(5))

print ("shared_times_n(-0.5)",shared_times_n(-0.5))

In [ ]:
#поменяем значение shared_vector_1
shared_vector_1.set_value([-1,0,1])
print ("shared:", shared_vector_1.get_value())

print ("shared_times_n(5)",shared_times_n(5))

print ("shared_times_n(-0.5)",shared_times_n(-0.5))

T.grad, самое вкусное

  • theano умеет само считать производные. Все, которые существуют.
  • Производные считаются в символическом, а не численном виде

Ограничения

  • За раз можно считать производную скалярной функции по одной или нескольким скалярным или векторным аргументам
  • Функция должна на всех этапах своего вычисления иметь тип float32 или float64 (т.к. на множестве целых чисел производная не имеет смысл)

In [12]:
my_scalar = T.scalar(name='input',dtype='float64')

scalar_squared = T.sum(my_scalar**2)

#производная v_squared по my_vector
derivative = T.grad(scalar_squared,my_scalar)

fun = theano.function([my_scalar],scalar_squared)
grad = theano.function([my_scalar],derivative)

In [13]:
import matplotlib.pyplot as plt
%matplotlib inline


x = np.linspace(-3,3)
x_squared = list(map(fun,x))
x_squared_der = list(map(grad,x))

plt.plot(x, x_squared,label="x^2")
plt.plot(x, x_squared_der, label="derivative")
plt.legend()


Out[13]:
<matplotlib.legend.Legend at 0x7fc8d17645d0>

теперь сам


In [14]:
my_vector = T.vector('float64')

#посчитай производные этой функции по my_scalar и my_vector
#warning! Не пытайся понять физический смысл этой функции
weird_psychotic_function = ((my_vector+my_scalar)**(1+T.var(my_vector)) +1./T.arcsinh(my_scalar)).mean()/(my_scalar**2 +1) + 0.01*T.sin(2*my_scalar**1.5)*(T.sum(my_vector)* my_scalar**2)*T.exp((my_scalar-4)**2)/(1+T.exp((my_scalar-4)**2))*(1.-(T.exp(-(my_scalar-4)**2))/(1+T.exp(-(my_scalar-4)**2)))**2


der_by_scalar,der_by_vector = градиент функции сверху по скаляру и вектору (можно дать списком)


compute_weird_function = theano.function([my_scalar,my_vector],weird_psychotic_function)
compute_der_by_scalar = theano.function([my_scalar,my_vector],der_by_scalar)

In [15]:
#график функции и твоей производной
vector_0 = [1,2,3]

scalar_space = np.linspace(0,7)

y = [compute_weird_function(x,vector_0) for x in scalar_space]
plt.plot(scalar_space,y,label='function')
y_der_by_scalar = [compute_der_by_scalar(x,vector_0) for x in scalar_space]
plt.plot(scalar_space,y_der_by_scalar,label='derivative')
plt.grid();plt.legend()


Out[15]:
<matplotlib.legend.Legend at 0x7fc8cd812e10>

Последний штрих - Updates

  • updates - это способ изменять значения shared переменных каждый раз В КОНЦЕ вызова функции

  • фактически, это словарь {shared_переменная: рецепт нового значения}, который добавляется в функцию при компиляции

Например,


In [ ]:
#умножим shared вектор на число и сохраним новое значение обратно в этот shared вектор

inputs = [input_scalar]
outputs = [scalar_times_shared] #вернём вектор, умноженный на число

my_updates = {
    shared_vector_1:scalar_times_shared #и этот же результат запишем в shared_vector_1
}

compute_and_save = theano.function(inputs, outputs, updates=my_updates)

In [ ]:
shared_vector_1.set_value(np.arange(5))

#изначальное значение shared_vector_1
print ("initial shared value:" ,shared_vector_1.get_value())

# теперь вычислим функцию (значение shared_vector_1 при этом поменяется)
print ("compute_and_save(2) returns",compute_and_save(2))

#проверим, что в shared_vector_1
print ("new shared value:" ,shared_vector_1.get_value())

Логистическая регрессия

Что нам потребуется:

  • Веса лучше хранить в shared-переменной
  • Данные можно передавать как input
  • Нужно 2 функции:
    • train_function(X,y) - возвращает ошибку и изменяет веса на 1 шаг по граиденту (через updates)
    • predict_fun(X) - возвращает предсказанные ответы ("y") по данным

In [ ]:
from sklearn.datasets import load_digits
mnist = load_digits(2)

X,y = mnist.data, mnist.target


print ("y [форма - %s]:"%(str(y.shape)),y[:10])

print ("X [форма - %s]:"%(str(X.shape)))
print (X[:3])

In [ ]:
# переменные и входы
shared_weights = <твой код>
input_X = <твой код>
input_y = <твой код>

In [ ]:
predicted_y = <предсказание логрегрессии на input_X (вероятность класса)>
loss = <логистическая ошибка (число - среднее по выборке)>

grad = <градиент loss по весам модели>



updates = {
    shared_weights: <новое значение весов после шага градиентного спуска>
}

In [ ]:
train_function = <функция, которая по X и Y возвращает ошибку и обновляет веса>
predict_function = <функция, которая по X считает предсказание для y>

In [ ]:
from sklearn.cross_validation import train_test_split
X_train,X_test,y_train,y_test = train_test_split(X,y)

In [ ]:
from sklearn.metrics import roc_auc_score

for i in range(5):
    loss_i = train_function(X_train,y_train)
    print ("loss at iter %i:%.4f"%(i,loss_i))
    print ("train auc:",roc_auc_score(y_train,predict_function(X_train)))
    print ("test auc:",roc_auc_score(y_test,predict_function(X_test)))

    
print ("resulting weights:")
plt.imshow(shared_weights.get_value().reshape(8,-1))
plt.colorbar()

lasagne

  • lasagne - это библиотека для написания нейронок произвольной формы на theano
  • библиотека низкоуровневая, границы между theano и lasagne практически нет

В качестве демо-задачи выберем то же распознавание чисел, но на большем масштабе задачи

  • картинки 28x28
  • 10 цифр

In [17]:
from mnist import load_dataset
X_train,y_train,X_val,y_val,X_test,y_test = load_dataset()

print (X_train.shape,y_train.shape)


(50000, 1, 28, 28) (50000,)

In [18]:
plt.imshow(X_train[0,0])


Out[18]:
<matplotlib.image.AxesImage at 0x7fc8d15428d0>

In [19]:
import lasagne

input_X = T.tensor4("X")

#размерность входа (None означает "может изменяться")
input_shape = [None,1,28,28]

target_y = T.vector("target Y integer",dtype='int32')

Так задаётся архитектура нейронки


In [20]:
#входной слой (вспомогательный)
input_layer = lasagne.layers.InputLayer(shape = input_shape,input_var=input_X)

#полносвязный слой, который принимает на вход input layer и имеет 100 нейронов.
# нелинейная функция - сигмоида как в логистической регрессии
# слоям тоже можно давать имена, но это необязательно
dense_1 = lasagne.layers.DenseLayer(input_layer,num_units=50,
                                   nonlinearity = lasagne.nonlinearities.sigmoid,
                                   name = "hidden_dense_layer")

#ВЫХОДНОЙ полносвязный слой, который принимает на вход dense_1 и имеет 10 нейронов -по нейрону на цифру
#нелинейность - softmax - чтобы вероятности всех цифр давали в сумме 1
dense_output = lasagne.layers.DenseLayer(dense_1,num_units = 10,
                                        nonlinearity = lasagne.nonlinearities.softmax,
                                        name='output')

In [21]:
#предсказание нейронки (theano-преобразование)
y_predicted = lasagne.layers.get_output(dense_output)

In [25]:
#все веса нейронки (shared-переменные)
all_weights = lasagne.layers.get_all_params(dense_output)
print (all_weights)


[hidden_dense_layer.W, hidden_dense_layer.b, output.W, output.b]

In [ ]:

дальше вы могли бы просто

  • задать функцию ошибки вручную
  • посчитать градиент ошибки по all_weights
  • написать updates
  • но это долго, а простой шаг по градиенту - не самый лучший смособ оптимизировать веса

Вместо этого, опять используем lasagne


In [30]:
#функция ошибки - средняя кроссэнтропия
loss = lasagne.objectives.categorical_crossentropy(y_predicted,target_y).mean()


accuracy = lasagne.objectives.categorical_accuracy(y_predicted,target_y).mean()

#сразу посчитать словарь обновлённых значений с шагом по градиенту, как раньше
updates_sgd = lasagne.updates.rmsprop(loss, all_weights,learning_rate=0.01)

In [31]:
#функция, которая обучает сеть на 1 шаг и возвращащет значение функции потерь и точности
train_fun = theano.function([input_X,target_y],[loss,accuracy],updates= updates_sgd)

#функция, которая считает точность
accuracy_fun = theano.function([input_X,target_y],accuracy)

Вот и всё, пошли её учить

  • данных теперь много, поэтому лучше учиться стохастическим градиентным спуском
  • для этого напишем функцию, которая бьёт выпорку на мини-батчи (в обычном питоне, не в theano)

In [39]:
# вспомогательная функция, которая возвращает список мини-батчей для обучения нейронки

#на вход
# X - тензор из картинок размером (много, 1, 28, 28), например - X_train
# y - вектор из чиселок - ответов для каждой картинки из X; например - Y_train
#batch_size - одно число - желаемый размер группы

#что нужно сделать
# 1) перемешать данные
# - важно перемешать X и y одним и тем же образом, чтобы сохранить соответствие картинки ответу на неё
# 3) побить данные на подгруппы так, чтобы в каждой подгруппе было batch_size картинок и ответов
# - если число картинок не делится на batch_size, одну подгруппу можно вернуть другого размера
# 4) вернуть список (или итератор) пар:
# - (подгруппа картинок, ответы из y на эту подгруппу)
def iterate_minibatches(X, y, batchsize):
    
    
    
    
    
    return X_minibatches, Y_minibatches # можно сделать списки, а ещё лучше - генератором через yield 
    
    
    
    
        
        
#
#
#
#
#
#
#
# Всё плохо и ты не понимаешь, что от тебя хотят?
# можешь поискать похожую функцию в примере
# https://github.com/Lasagne/Lasagne/blob/master/examples/mnist.py

Процесс обучения


In [40]:
import time

num_epochs = 100 #количество проходов по данным

batch_size = 50 #размер мини-батча

for epoch in range(num_epochs):
    # In each epoch, we do a full pass over the training data:
    train_err = 0
    train_acc = 0
    train_batches = 0
    start_time = time.time()
    for batch in iterate_minibatches(X_train, y_train,batch_size):
        inputs, targets = batch
        train_err_batch, train_acc_batch= train_fun(inputs, targets)
        train_err += train_err_batch
        train_acc += train_acc_batch
        train_batches += 1

    # And a full pass over the validation data:
    val_acc = 0
    val_batches = 0
    for batch in iterate_minibatches(X_val, y_val, batch_size):
        inputs, targets = batch
        val_acc += accuracy_fun(inputs, targets)
        val_batches += 1

    
    # Then we print the results for this epoch:
    print("Epoch {} of {} took {:.3f}s".format(
        epoch + 1, num_epochs, time.time() - start_time))

    print("  training loss (in-iteration):\t\t{:.6f}".format(train_err / train_batches))
    print("  train accuracy:\t\t{:.2f} %".format(
        train_acc / train_batches * 100))
    print("  validation accuracy:\t\t{:.2f} %".format(
        val_acc / val_batches * 100))


Epoch 1 of 100 took 6.061s
  training loss (in-iteration):		0.291875
  train accuracy:		91.36 %
  validation accuracy:		95.56 %
Epoch 2 of 100 took 5.314s
  training loss (in-iteration):		0.143604
  train accuracy:		95.73 %
  validation accuracy:		96.11 %
Epoch 3 of 100 took 5.328s
  training loss (in-iteration):		0.112671
  train accuracy:		96.60 %
  validation accuracy:		96.58 %
Epoch 4 of 100 took 5.214s
  training loss (in-iteration):		0.093298
  train accuracy:		97.11 %
  validation accuracy:		96.58 %
Epoch 5 of 100 took 5.239s
  training loss (in-iteration):		0.082499
  train accuracy:		97.45 %
  validation accuracy:		96.73 %
Epoch 6 of 100 took 5.218s
  training loss (in-iteration):		0.074038
  train accuracy:		97.70 %
  validation accuracy:		96.51 %
Epoch 7 of 100 took 5.245s
  training loss (in-iteration):		0.064788
  train accuracy:		97.96 %
  validation accuracy:		96.91 %
Epoch 8 of 100 took 5.486s
  training loss (in-iteration):		0.059453
  train accuracy:		98.14 %
  validation accuracy:		96.88 %
---------------------------------------------------------------------------
KeyboardInterrupt                         Traceback (most recent call last)
<ipython-input-40-7b90d953a9de> in <module>()
     13     for batch in iterate_minibatches(X_train, y_train,batch_size):
     14         inputs, targets = batch
---> 15         train_err_batch, train_acc_batch= train_fun(inputs, targets)
     16         train_err += train_err_batch
     17         train_acc += train_acc_batch

/usr/local/lib/python2.7/dist-packages/theano/compile/function_module.pyc in __call__(self, *args, **kwargs)
    857         t0_fn = time.time()
    858         try:
--> 859             outputs = self.fn()
    860         except Exception:
    861             if hasattr(self.fn, 'position_of_error'):

KeyboardInterrupt: 

In [41]:
test_acc = 0
test_batches = 0
for batch in iterate_minibatches(X_test, y_test, 500):
    inputs, targets = batch
    acc = accuracy_fun(inputs, targets)
    test_acc += acc
    test_batches += 1
print("Final results:")
print("  test accuracy:\t\t{:.2f} %".format(
    test_acc / test_batches * 100))

if test_acc / test_batches * 100 > 99:
    print ("Achievement unlocked: колдун 80 уровня")
else:
    print ("Нужно больше магии!")


Final results:
  test accuracy:		96.66 %
Нужно больше магии!

Нейронка твоей мечты

  • Задача - сделать нейронку, которая получит точность 99% на валидации (validation accuracy)
    • +1 балл за каждые 0.2% сверх 99%
    • Вариант "is fine too" - 97.5%.
    • Чем выше, тем лучше.

В конце есть мини-отчётик, который имеет смысл прочитать вначале и заполнять по ходу работы.

Что можно улучшить:

  • размер сети
    • бОльше нейронов,
    • бОльше слоёв,
    • почти наверняка нужны свёртки
    • Пх'нглуи мглв'нафх Ктулху Р'льех вгах'нагл фхтагн!
  • Метод оптимизации - rmsprop, nesterov_momentum, adadelta, adagrad и т.п.
    • сходятся быстрее и иногда - к лучшему оптимуму
    • имеет смысл поиграть с размером батча, количеством эпох и скоростью обучения
  • Dropout - для борьбы с переобучением
    • lasagne.layers.DropoutLayer(предыдущий_слой, p=вероятность_занулить)
  • Свёрточные слои

    • network = lasagne.layers.Conv2DLayer(предыдущий_слой, num_filters = число нейронов, filter_size = (ширина_квадрата, высота_квадрата), nonlinearity = нелинейная_функция)
    • ВАРНУНГ! могут учиться долго на CPU
      • Однако мы всё равно рекоммендуем обучить хотя бы маленькую свёртку
  • Любые другие слои и архитектуры

  • Наконец, можно поиграть с нелинейностями в скрытых слоях
    • tanh, relu, leaky relu, etc

Для удобства, ниже есть заготовка решения, которое можно заполнять, а можно выкинуть и написать своё


In [ ]:
from mnist import load_dataset
X_train,y_train,X_val,y_val,X_test,y_test = load_dataset()

print (X_train.shape,y_train.shape)

In [ ]:
import lasagne

input_X = T.tensor4("X")

#размерность входа (None означает "может изменяться")
input_shape = [None,1,28,28]

target_y = T.vector("target Y integer",dtype='int32')

In [ ]:
#входной слой (вспомогательный)
input_layer = lasagne.layers.InputLayer(shape = input_shape,input_var=input_X)


<моя архитектура>

#ВЫХОДНОЙ полносвязный слой, который принимает на вход dense_1 и имеет 10 нейронов -по нейрону на цифру
#нелинейность - softmax - чтобы вероятности всех цифр давали в сумме 1
dense_output = lasagne.layers.DenseLayer(<предвыходной_слой>,num_units = 10,
                                        nonlinearity = lasagne.nonlinearities.softmax,
                                        name='output')

In [ ]:
#предсказание нейронки (theano-преобразование)
y_predicted = lasagne.layers.get_output(dense_output)

In [ ]:
#все веса нейронки (shared-переменные)
all_weights = lasagne.layers.get_all_params(dense_output)
print (all_weights)

In [ ]:
#функция ошибки - средняя кроссэнтропия
loss = lasagne.objectives.categorical_crossentropy(y_predicted,target_y).mean()

#<возможно добавить регуляризатор>

accuracy = lasagne.objectives.categorical_accuracy(y_predicted,target_y).mean()

#сразу посчитать словарь обновлённых значений с шагом по градиенту, как раньше
updates_sgd = <поиграться с методами>

In [ ]:
#функция, которая обучает сеть на 1 шаг и возвращащет значение функции потерь и точности
train_fun = theano.function([input_X,target_y],[loss,accuracy],updates= updates_sgd)

#функция, которая считает точность
accuracy_fun = theano.function([input_X,target_y],accuracy)

In [ ]:
#итерации обучения

num_epochs = сколько_эпох #количество проходов по данным

batch_size = сколько_картинок_в_минибатче #размер мини-батча

for epoch in range(num_epochs):
    # In each epoch, we do a full pass over the training data:
    train_err = 0
    train_acc = 0
    train_batches = 0
    start_time = time.time()
    for batch in iterate_minibatches(X_train, y_train,batch_size):
        inputs, targets = batch
        train_err_batch, train_acc_batch= train_fun(inputs, targets)
        train_err += train_err_batch
        train_acc += train_acc_batch
        train_batches += 1

    # And a full pass over the validation data:
    val_acc = 0
    val_batches = 0
    for batch in iterate_minibatches(X_val, y_val, batch_size):
        inputs, targets = batch
        val_acc += accuracy_fun(inputs, targets)
        val_batches += 1

    
    # Then we print the results for this epoch:
    print("Epoch {} of {} took {:.3f}s".format(
        epoch + 1, num_epochs, time.time() - start_time))

    print("  training loss (in-iteration):\t\t{:.6f}".format(train_err / train_batches))
    print("  train accuracy:\t\t{:.2f} %".format(
        train_acc / train_batches * 100))
    print("  validation accuracy:\t\t{:.2f} %".format(
        val_acc / val_batches * 100))

In [ ]:
test_acc = 0
test_batches = 0
for batch in iterate_minibatches(X_test, y_test, 500):
    inputs, targets = batch
    acc = accuracy_fun(inputs, targets)
    test_acc += acc
    test_batches += 1
print("Final results:")
print("  test accuracy:\t\t{:.2f} %".format(
    test_acc / test_batches * 100))

Отчётик, примерный его вид.

Творческий подход приветствуется, но хотелось бы узнать про следующие вещи:

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

Строгих математических выводов от вас никто не ждёт, вариант

  • "Попробовал так, получилось лучше, чем вот-так, а тот третий вариант по названию не понравился" - не предел мечты, но ок
  • "Почитал такие статьи, сделал такие эксперименты, пришёл к такому выводу" - _идеально
  • "сделал так, потому что в вон-той демке другой чувак так сделал, но тебе об этом не скажу, а придумаю какую-нибудь наукообразную чушь" - не ок

Привет, я ___ ___, и вот моя история

Когда-то давно, когда трава была зеленее, а до дедлайна ещё оставалось больше часа, мне в голову пришла идея:

А давай, я сделаю нейронку, которая
  • немного текста
  • про то, что
  • и как ты учишь,
  • почему именно так

Так мне казалось.

В один прекрасный день, когда ничего не предвещало беды,

Эта злыдня, наконец, доучилась, как вдруг

  • немного текста
  • про то, что получилось в итоге
  • были ли приняты какие-то изменения и почему
  • если да - к чему они привели
И вот, спустя __ попыток, на свет появилась
  • описание финальной сети

Которая, после стольких мук, __ [минут, часов или дней - по вкусу] обучения дала-таки точность

  • точность - на обучении
  • точность - на валидации
  • точность - на тесте

[опциональное послесловие и пожелания автору задания сдохнуть в страшных муках]


In [ ]: