Lasagne не работает: Библиотеки для глубокого обучения Theano/Lasagne / Хабр

Библиотеки для глубокого обучения Theano/Lasagne / Хабр

Привет, Хабр!

Параллельно с публикациями статей открытого курса по машинному обучению мы решили запустить ещё одну серию — о работе с популярными фреймворками для нейронных сетей и глубокого обучения.

Я открою этот цикл статьёй о Theano — библиотеке, которая используется для разработки систем машинного обучения как сама по себе, так и в качестве вычислительного бекэнда для более высокоуровневых библиотек, например, Lasagne, Keras или Blocks.

Theano разрабатывается с 2007 года главным образом группой MILA из Университета Монреаля и названа в честь древнегреческой женщины-философа и математика Феано (предположительно изображена на картинке). Основными принципами являются: интеграция с numpy, прозрачное использование различных вычислительных устройств (главным образом GPU), динамическая генерация оптимизированного С-кода.


Будем придерживаться следующего плана:


  • Предисловие или лирическое отступление о библиотеках для глубокого обучения
  • Введение
    • Установка
    • Настройка
  • Основы
    • Первые шаги
    • Переменные и функции
    • Разделяемые переменные и еще о функциях
    • Отладка
  • Машинное обучение на Theano
    • Логистическая регрессия
    • SVM
    • Нелинейные признаки
  • Lasagne
    • Многослойный перцептрон
  • Заключение

Код с примерами из этого поста можно найти тут.


Предисловие или лирическое отступление о библиотеках для глубокого обучения

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

Давайте посмотрим на примере, чем они различаются. Предположим, что мы хотим вычислить простое выражение

Вот так оно выглядело бы в императивном изложении на языке python:

    a = np.ones(10)
    b = np.ones(10) * 2
    c = b * a
    d = c + 1

Интерпретатор исполняет код построчно, сохраняя результаты в переменных a, b, c и d.
Та же программа в символьной парадигме выглядела бы так:

    A = Variable('A')
    B = Variable('B')
    C = B * A
    D = C + Constant(1)
    # компиляция функции
    f = compile(D)
    # исполнение
    d = f(A=np.ones(10), B=np.ones(10)*2)

Существенное различие заключается в том, что когда мы объявляем D, исполнения не происходит, мы лишь задали граф вычислений, который затем скомпилировали и наконец выполнили.

Оба подхода имеют свои достоинства и недостатки. В первую очередь, императивные программы гибче, нагляднее и проще в отладке. Мы можем использовать все богатства используемого языка программирования, например, циклы и ветвления, выводить промежуточные результаты в отладочных целях. Такая гибкость достигается, в первую очередь, малыми ограничениями, накладываемыми на интерпретатор, который должен быть готов к любому последующему использованию переменных.

С другой стороны, символьная парадигма накладывает больше ограничений, но вычисления получаются более эффективными, как по памяти, так и по скорости исполнения: на этапе компиляции можно применить ряд оптимизаций, выявить неиспользуемые переменные, выполнить часть вычислений, переиспользуя память и так далее. Отличительная черта символьных программ — отдельные этапы объявления графа, компиляции и выполнения.

Мы останавливаемся на этом так подробно, потому что императивная парадигма знакома большинству программистов, в то время как символьная может показаться непривычной, и Theano, как раз, явный пример символьного фреймворка.

Тем, кому хочется разобраться в этом вопросе подробнее, рекомендую почитать соответствующий раздел документации к MXNet (об этой библиотеке мы еще напишем отдельный пост), но ключевой момент для понимания дальнейшего текста заключается в том, что программируя на Theano, мы пишем на python программу, которую потом скомпилируем и выполним.
Но довольно теории, давайте разбираться с Theano на примерах.


Установка

Для установки нам понадобятся: python версии старше 2.6 или 3.3 (лучше dev-версию), компилятор С++ (g++ для Linux или Windows, clang для MacOS), библиотека примитивов линейной алгебры (например ATLAS, OpenBLAS, Intel MKL), NumPy и SciPy.

Для выполнения вычислений на GPU понадобится CUDA, а ряд операций, встречающихся в нейронных сетях, можно ускорить с помощью сuDNN. Начиная с версии 0.8.0, разработчики Theano рекомендуют использовать libgpuarray, что также даёт возможность использовать несколько GPU.

Когда все зависимости установлены, можно установить Theano через pip:

# последний релиз
pip install Theano
# последняя версия из репозитория
pip install --upgrade https://github. com/Theano/Theano/archive/master.zip

Настройка

Theano можно настроить тремя способами:


  • Выставив атрибуты объекта theano.config в нужное значение
  • Через переменную окружения THEANO_FLAGS
  • Через конфигурационный файл $HOME/.theanorc (или $HOME/.theanorc.txt под Windows)

Я обычно использую примерно такой конфигурационный файл:

[global]
    device = gpu # выбирает устройство, на котором будет выполняться наш код - GPU или CPU
    floatX = float32
    optimizer_including=cudnn
    allow_gc = False # быстрее, но использует больше памяти
    #exception_verbosity=high
    #optimizer = None  # полезно при отладке
    #profile = True
    #profile_memory = True
config.dnn.conv.algo_fwd = time_once # эти две опции зачастую приводят к ускорению свёрток
config.dnn.conv.algo_bwd = time_once
[lib]
Cnmem = 0.95 # позволяет включить CNMeM (https://github.com/NVIDIA/cnmem) - менеджер CUDA-памяти

Подробнее о конфигурации можно узнать в документации.


Основы


Первые шаги

Теперь, когда всё установлено и настроено, давайте попробуем написать немного кода, например, вычислим значение многочлена в точке 10:

import theano 
import theano.tensor as T
# объявим theano-переменную
a = T.lscalar()
# определим выражение
expression = 1 + 2 * a + a ** 2
# скомпилируем theano-функцию
f = theano.function(
    inputs=[a],        # аргументы
    outputs=expression  # результат
)
# запустим вычисление
f(10)
>>> array(121)

Здесь мы совершили 4 вещи: определили скалярную переменную а типа long, создали выражение, содержащее наш многочлен, определили и скомпилировали функцию f, а также выполнили её, передав на вход число 10.

Обратим внимание на тот факт, что переменные в Theano — типизированные, причем тип переменной содержит информацию как о типе данных, так и о их размерности, т.е. чтобы посчитать наш многочлен сразу в нескольких точках, потребуется определить а как вектор:

a = T. lvector()
expression = 1 + 2 * a + a ** 2
f = theano.function(
    inputs=[a],
    outputs=expression
)
arg = arange(-10, 10)
res = f(arg)
plot(arg, res, c='m', linewidth=3.)

В данном случае нам нужно только указать количество измерений переменной при инициализации: размер каждого измерения вычисляется автоматически на этапе вызова функции.

UPD: Не секрет, что аппарат линейной алгебры повсеместно используется в машинном обучении: примеры описываются векторами признаков, параметры модели записывают в виде матриц, изображения представляют в виде 3х-мерных тензоров. Скалярные величины, векторы и матрицы можно рассматривать как частный случай тензоров, поэтому именно так мы в дальнейшем будем называть эти объекты линейной алгебры. Под тензором будем понимать N-мерные массивы чисел.
Пакет theano.tensor содержит наиболее часто употребляемые типы тензоров, однако, нетрудно определить и свой тип.

При несовпадении типов Theano выбросит исключение. Исправить это, кстати как и поменять многое другое в работе функций, можно, передав конструктору аргумент

allow_input_downcast=True:

x = T.dmatrix('x')
v = T.fvector('v')
z = v + x
f = theano.function(
    inputs=[x, v],
    outputs=z,
    allow_input_downcast=True
)
f_fail = theano.function(
    inputs=[x, v],
    outputs=z
)
print(f(ones((3, 4), dtype=float64), ones((4,), dtype=float64))
>>> [[ 2.  2.  2.  2.]
>>>  [ 2.  2.  2.  2.]
>>>  [ 2.  2.  2.  2.]]
print(f_fail(ones((3, 4), dtype=float64), ones((4,), dtype=float64))
>>> ---------------------------------------------------------------------------
>>> TypeError                                 Traceback (most recent call last)

Мы также можем вычислять несколько выражений сразу, оптимизатор в этом случае может переиспользовать пересекающиеся части, в данном случае сумму :

x = T.lscalar('x')
y = T.lscalar('y')
square = T.square(x + y)
sqrt = T.sqrt(x + y)
f = theano.
function( inputs=[x, y], outputs=[square, sqrt] ) print(f(5, 4)) >>> [array(81), array(3.0)] print(f(2, 2)) >>> [array(16), array(2.0)]

Для обмена состояниями между функциями используются специальные shared переменные:

state = theano.shared(0)
i = T.iscalar('i')
inc = theano.function([i],
                      state,
                      # обновим разделяемую переменную
                      updates=[(state, state+i)])
dec = theano.function([i],
                      state,
                      updates=[(state, state-i)])
# разделяемые переменные могут менять значения сразу нескольких функций
print(state.get_value())
inc(1)
inc(1)
inc(1)
print(state.get_value())
dec(2)
print(state.get_value())
>>> 0
>>> 3
>>> 1

Значения таких переменных, в отличие от тензорных, можно получать и модифицировать вне Theano-функций из обычного python-кода:

state.set_value(-15)
print(state.get_value())
>>> -15

Значения в shared переменных можно «подставлять» в тензорные переменные:

x = T. lscalar('x')
y = T.lscalar('y')
i = T.lscalar('i')
expression = (x - y) ** 2
state = theano.shared(0)
f = theano.function(
    inputs=[x, i],
    outputs=expression,
    updates=[(state, state+i)],
    # подставим значение переменной state в граф вместо y
    givens={
        y : state
    }
)
print(f(5, 1))
>>> 25
print(f(2, 1))
>>> 1

Отладка

Theano предоставляет ряд средств для отображения графа вычислений и отладки. Тем не менее, отладка символьных выражений по-прежнему остаётся задачей не из лёгких. Мы кратко перечислим тут наиболее употребимые подходы, подробнее об отладке можно прочитать в документации: http://deeplearning.net/software/theano/tutorial/printing_drawing.html

Мы можем распечатать граф вычислений для каждой функции:

x = T.lscalar('x')
y = T.lscalar('y')
square = T.square(x + y)
sqrt = T.sqrt(x + y)
f = theano.function(
    inputs=[x, y],
    outputs=[square, sqrt]
)
# сумма будет подсчитана только один раз
theano.printing. debugprint(f)

Заметьте, что сумма вычисляется только один раз:

Elemwise{Sqr}[(0, 0)] [id A] ''   2
 |Elemwise{add,no_inplace} [id B] ''   0
   |x [id C]
   |y [id D]
Elemwise{sqrt,no_inplace} [id E] ''   1
 |Elemwise{add,no_inplace} [id B] ''   0

Выражения можно выводить и в более лаконичной форме:

# определим выражение
W = T.fmatrix('W')
b = T.fvector('b')
X = T.fmatrix('X')
expr = T.dot(X, W) + b
prob = 1 / (1 + T.exp(-expr))
pred = prob > 0.5
# и распечатаем его
theano.pprint(pred)
>>> 'gt((TensorConstant{1} / (TensorConstant{1} + exp((-((X \\dot W) + b))))), TensorConstant{0.5})'

Или в виде графа:

theano.printing.pydotprint(pred, outfile='pics/pred_graph.png', var_with_name_simple=True)


К сожалению, читаемость таких графов резко падает с ростом сложности выражения. Фактически что-то понять можно только на игрушечных примерах.


Машинное обучение на Theano


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

Давайте посмотрим на примере логистической регрессии, как с помощью Theano можно разрабатывать алгоритмы машинного обучения. Намеренно не будем вдаваться в подробности того, как устроена эта модель (оставим это до соответствующей статьи открытого курса), но напомним, что апостериорная вероятность класса имеет вид

Давайте определим параметры модели, для удобства введём отдельный параметр для смещения:

W = theano.shared(
    value=numpy.zeros((2, 1),dtype=theano.config.floatX),
    name='W')
b = theano.shared(
    value=numpy.zeros((1,), dtype=theano.config.floatX),
    name='b')

И заведём символьные переменные для признаков и меток класса:

X = T.matrix('X')
Y = T.imatrix('Y')

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

linear = T.dot(X, W) + b
p_y_given_x = T.nnet.sigmoid(linear)
y_pred = p_y_given_x > 0.5

И определим функцию потерь вида:

loss = T.nnet.binary_crossentropy(p_y_given_x, Y).mean()

Мы не стали выписывать выражения для сигмоиды и кросс-энтропии в явном виде, а воспользовались функциями из пакета theano. tensor.nnet, который предоставляет оптимизированные реализации ряда популярных в машинном обучении функций. Кроме того, функции из этого пакета обычно включают в себя дополнительные трюки для численной устойчивости.

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

Давайте воплотим его в коде:

g_W = T.grad(loss, W)
g_b = T.grad(loss, b)
updates = [(W, W - 0.04 * g_W),
           (b, b - 0.08 * g_b)]

Здесь мы воспользовались замечательной возможностью Theano — автоматическим2 дифференцированием. Вызов T.grad вернул нам выражение, которое будет содержать градиент первого аргумента по второму. Это может показаться излишним для столь простого случая, но очень выручает при построении больших, многослойных моделей.

Когда градиенты получены, нам остаётся лишь скомпилировать Theano-функции:

train = theano.function(
    inputs=[X, Y],
    outputs=loss,
    updates=updates,
    allow_input_downcast=True
)
predict_proba = theano. function(
    [X],
    p_y_given_x,
    allow_input_downcast=True
)

И запустить итеративный процесс:

sgd_weights = [W.get_value().flatten()]
for iter_ in range(4001):
    loss = train(x, y[:, np.newaxis])
    sgd_weights.append(W.get_value().flatten())
    if iter_ % 100 == 0:
        print("[Iteration {:04d}] Train loss: {:.4f}".format(iter_, float(loss)))

Для сгенерированных мною данных процесс сходится к такой разделяющей прямой:

Выглядит неплохо, но кажется, что для такой простой задачи 4000 итераций — это как-то многовато… Давайте попробуем ускорить оптимизацию и воспользуемся методом Ньютона. Этот метод использует вторые производные функции потерь и представляет собой последовательность таких шагов:

где — матрица Гессе.

Чтобы посчитать матрицу Гессе, создадим одномерные версии параметров нашей модели:

W_init = numpy.zeros((2,),dtype=theano.config.floatX)
W_flat = theano.shared(W_init, name='W')
W = W_flat.reshape((2, 1))
b_init = numpy. zeros((1,), dtype=theano.config.floatX)
b_flat = theano.shared(b_init, name='b')
b = b_flat.reshape((1,))

И определим шаг оптимизатора:

h_W = T.nlinalg.matrix_inverse(theano.gradient.hessian(loss, wrt=W_flat))
h_b = T.nlinalg.matrix_inverse(theano.gradient.hessian(loss, wrt=b_flat))
updates_newton = [(W_flat, W_flat -  T.dot(h_W , g_W)),
           (b_flat, b_flat - T.dot(h_b, g_b))]

Хоть мы и пришли к тем же результатам,
,
Методу Ньютона для этого понадобилось всего 30 (против 4000 у градиентного спуска) шагов.

Пути обоих методов можно посмотреть на этом графике:


SVC

Так же мы без труда можем реализовать метод опорных векторов, для этого достаточно представить функцию потерь в следующем виде:

В терминах Theano это можно написать с помощью замены нескольких строк в предыдущем примере:

C = 10.
loss = C * T.maximum(0, 1 - linear * (Y * 2 - 1)).mean() + T.square(W).sum()
predict = theano.function(
    [X],
    linear > 0,
    allow_input_downcast=True
)

это гиперпараметр, регуляризующий модель, а выражение просто переводит метки в диапазон

Для выбранного С классификатор разделит пространство так:


Нелинейные признаки

Циклы являются одной из наиболее употребимых конструкций в программировании. Поддержка циклов в Theano представлена функцией scan. Давайте познакомимся с тем, как она работает. Думаю, читателям уже очевидно, что линейная функция от признаков — не лучший кандидат на разделение сгенерированных данных. Этот недостаток можно исправить, добавив полиномиальные признаки к исходным (этот приём подробно описан в другой статье нашего блога). Итак, хочется получить преобразование вида . В python мы могли бы реализовать его, например, так:

poly = []
for i in range(K):
   poly.extend([x**i for x in features])

В Theano это выглядит следующим образом:

def poly(x, degree=2):
    result, updates = theano.scan(
                                  # записываем выражение, вычисляющее каждую новую степень полинома
                                  fn=lambda prior_result, x: prior_result * x,
                                  # инициализируем выходной тензор
                                  outputs_info=T.ones_like(x),
                                  # укажем, что x надо передать в качестве второго аргумента fn
                                  non_sequences=x,
                                  # количество итераций
                                  n_steps=degree)
    # результат возвращаем в виде матрицы N x M*degree
    return result. dimshuffle(1, 0, 2).reshape((result.shape[1], 
                                               result.shape[0] * result.shape[2]))

Первой в scan передается функция, которая будет вызываться на каждой итерации, её первый аргумент — результат на предыдущей итерации, последующие — все non_sequences; outputs_info инициализирует выходной тензор такой же размерности и типа, как и x, и заполняет его единицами; n_steps указывает на требуемое количество итераций.

scan вернет результат в виде тензора размера (n_steps, ) + outputs_info.shape, поэтому мы преобразуем его в матрицу, чтобы получить нужные признаки.

Проиллюстрируем работу полученного выражения простым примером:

[[1, 2],   ->  [[ 1,  2,  1,  4],
 [3, 4],   ->   [ 3,  4,  9, 16],
 [5, 6]]   ->   [ 5,  6, 25, 36]]

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

W = theano. shared(
    value=numpy.zeros((8, 1),dtype=theano.config.floatX), 
    name='W')
linear = T.dot(poly(X, degree=4), W) + b

Новые признаки позволяют значительно лучше разделить классы:


Нейронные сети и Lasagne

3

К этому моменту мы уже обсудили основные этапы создания систем машинного обучения на Theano: инициализация входных переменных, определение модели, компиляция Theano-функций, цикл с шагами оптимизатора. На этом можно было бы и закончить, но очень уж хочется познакомить читателей с Lasagne — замечательной библиотекой для нейронных сетей, работающей поверх Theano. Lasagne предоставляет набор готовых компонентов: слоёв, алгоритмов оптимизации, функций потерь, инициализаций параметров и т.д., при этом не скрывает Theano за многочисленными слоями абстракций.

Рассмотрим, как может выглядеть типичный код на Theano/Lasagne на примере классификации MNIST’a.


Примеры изображений из MNIST, если вы их еще не видели

Сконструируем многослойный перцептрон с двумя скрытыми слоями по 800 нейронов, для регуляризации будем использовать dropout и разместим этот код в отдельной функции:

def build_mlp(input_var=None):
    # Входной слой, определяющий размерность данных
    # (переменный размер minibatch'a, 1 канал, 28 строк и 28 столбов) 
    # Мы можем передать тензорную переменную для входных данных,
    # или же она будет создана для нас
    network = lasagne. layers.InputLayer(
        shape=(None, 1, 28, 28),
        input_var=input_var)
    #   dropout на 20% для входных данных
    network = lasagne.layers.DropoutLayer(network, p=0.2)
    # Полносвязный слой на 800 нейронов и ReLU в качестве нелинейности
    # также инициализируем веса методом, предложенным Xavier Glorot и Yoshua Bengio
    network = lasagne.layers.DenseLayer(
        network, 
        num_units=800,
        nonlinearity=lasagne.nonlinearities.rectify,
        W=lasagne.init.GlorotUniform())
    # Добавим dropout еще на 50%:
    network = lasagne.layers.DropoutLayer(network, p=0.5)
    # И ещё один полносвязный слой
    network = lasagne.layers.DenseLayer(
        network,
        num_units=800,
        nonlinearity=lasagne.nonlinearities.rectify)
    network = lasagne.layers.DropoutLayer(network, p=0.5)
    # Наконец, добавим классификатор на 10 классов:
    network = lasagne.layers.DenseLayer(
        network,
        num_units=10,
        nonlinearity=lasagne.nonlinearities. softmax)
    return network

Получим вот такую простую полносвязную сеть:



Инициализируем тензорные переменные и скомпилируем Theano-функции для обучения и валидации:

input_var = T.tensor4('inputs')
target_var = T.ivector('targets')
# воспользуемся функцией из предыдущего листинга
network = build_mlp(input_var)
# эта функция вернёт нам граф вычислений, соответствующий сети
prediction = lasagne.layers.get_output(network)
# зададим функцию потерь
loss = lasagne.objectives.categorical_crossentropy(prediction, target_var).mean()
# Сюда также можно добавить L1 или L2 регуляризацию, см. lasagne.regularization.
# Этот метод позволит получить список параметров сети
# он также принимает keyword аргумент, позволяющий выбрать параметры по тегу
# наиболее часто употребимые это trainable и regularizable
params = lasagne.layers.get_all_params(network, trainable=True)
# используем метод стохастического градиентного спуска с моментом Нестерова
updates = lasagne.updates. nesterov_momentum(
    loss, 
    params, 
    learning_rate=0.01, 
    momentum=0.9)
# Также создадим выражение для функции потерь на валидации. 
# Главное отличие тут заключается в аргументе deterministic=True,
# который отключает dropout
test_prediction = lasagne.layers.get_output(network, deterministic=True)
test_loss = T.nnet.categorical_crossentropy(test_prediction,
                                                        target_var).mean()
# Заодно посчитаем точность классификатора
test_acc = T.mean(
    T.eq(T.argmax(test_prediction, axis=1), target_var),
    dtype=theano.config.floatX)
# скомпилируем функцию для обучения
train = theano.function(
    inputs=[input_var, target_var],
    outputs=loss,
    updates=updates)
# и вторую — для валидации
# оптимизатор Theano тут поймёт, что для вычисления функции потерь и точности
# можно переиспользовать большую часть графа
validate = theano.function(
    inputs=[input_var, target_var],
    outputs=[test_loss, test_acc])

Теперь создадим цикл обучения:

print("| Epoch | Train err | Validation err | Accuracy |  Time  |")
print("|------------------------------------------------------------------------|")
try:
    for epoch in range(100):
            # Каждую эпоху будем проходить по всей обучающей выборке
            train_err = 0
            train_batches = 0
            start_time = time. time()
            for batch in iterate_minibatches(X_train, y_train, 500, shuffle=True):
                inputs, targets = batch
                train_err += train(inputs, targets)
                train_batches += 1
            # И по всей валидационной
            val_err = 0
            val_acc = 0
            val_batches = 0
            for batch in iterate_minibatches(X_val, y_val, 500, shuffle=False):
                inputs, targets = batch
                err, acc = validate(inputs, targets)
                val_err += err
                val_acc += acc
                val_batches += 1
            print("|{:05d} | {:4.5f} | {:16.5f} | {:10.2f} | {:7.2f} |".format
                  (epoch,
                    train_err / train_batches,
                    val_err / val_batches,
                    val_acc / val_batches * 100,
                    time.time() - start_time))
except KeyboardInterrupt:
    print("The training was interrupted on epoch: {}".format(epoch))

Получившиеся кривые обучения:



Нашей модели удаётся достичь точности более 98 %, что, несомненно, можно улучшить, используя, например, свёрточные нейронные сети, но эта тема уже выходит за рамки данной статьи.

Сохранять и загружать веса удобно с помощью хелперов:

# Сохраняем веса
savez('model.npz', *lasagne.layers.get_all_param_values(network))
network = build_mlp()
# И загружаем, когда потребуется:
with np.load('model.npz') as f:
     param_values = [f['arr_%d' % i] for i in range(len(f.files))]
lasagne.layers.set_all_param_values(network, param_values)

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


Заключение

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


  • обратившись к документации: 1, 2
  • посмотрев примеры кода
  • пообщавшись с сообществом
  • заглянув в исходный код.

Большая благодарность bauchgefuehl за помощь в подготовке поста.




1. Граница между двумя подходами довольно размыта, и не всё сказанное ниже строго верно, всегда есть исключения и пограничные случаи. Наша задача тут — передать основную идею.
2. Разработчики Theano в техническом отчёте и документации называют дифференцирование символьным. Однако, использование этого термина в одной из предыдущих статей на Хабр вызвало обсуждение. Исходя из исходного кода Theano и определения на Википедии, автор считает, что правильный термин всё же «автоматическое дифференцирование».
3. Материал этого раздела по большей части основан на документации к Lasagne.

Макаронные изделия Barilla Lasagne Bolognesi лазанья (450 г)

Не является публичной офертой


Производитель: Barilla G. & R. Fratelli S.p.A
Бренд: Barilla
Страна: ИТАЛИЯ
Цвет флакона / упаковки
Вес брутто:500 г.
Объём:850 см3.
Штрихкод:8076809523738
Куплено штук:10

Описание

Пищевая ценность (на 100гр):
ККал : 359 ккал
Б/Ж/У : 12,5 г / 2 г / 71,2 г

Болонья – город на севере Италии, который славится своим гостеприимством и разнообразием кулинарных традиций. Лазанья болоньезе наилучшим образом отражает всю суть этого города и всю его страсть. Прямоугольные листы лазаньи нарезаются из теста, раскатанного так тонко, что его текстура позволяет соусу равномерно распределиться по всей поверхности и в полной мере раскрыть вкус всех его ингредиентов. Листы лазаньи Barilla готовятся в духовке всего 20 минут и не требуют предварительного отваривания.

Лазанья, пожалуй, одно из самых популярных итальянских блюд: тесто прослаивается различными начинками – мясом, грибами, сыром, овощами или морепродуктами, а сочность блюду придает использование специальных соусов. Традиционная лазанья болоньезе готовится на основе двух соусов: мясного рагу «Болоньезе», и соуса на основе молока, муки и сливочного масла – «Бешамель», все слои щедро посыпаются тертым сыром.

Из высококачественной пшеницы твёрдых сортов
Готовится 20 минут
Отличный источник энергии
Низкий гликемический индекс: вы дольше остаетесь сытыми
Продукт богат железом и витаминами группы B

Подходит для диет, исключающих продукты:

Продукты животного происхождения
• Молочные продукты
• Рыба
• Продукты с содержанием лактозы
• Арахис
• Моллюски
• Орехи

Мука твердых сортов пшеницы категории А, вода

Товара нет в наличии ☹



Этот товар участвует в акции:

  • Продукция из гипермаркета МЕТРО Кэш энд Керри
Поделиться:


Можете ли вы собрать лазанью за один день, а затем испечь ее примерно через 24 часа?

спросил

Изменено 2 года, 10 месяцев назад

Просмотрено 277 тысяч раз

Я всегда готовил его на месте и тут же готовил, но хотел бы приготовить дома, взять на следующий день на большую вечеринку и потом приготовить — получится ли?

  • лазанья

Я готовил лазанью, а на следующий день разогревал. Если вы соберете лазанью, пока соус (соусы) теплый, вы обнаружите, что макароны начинают сворачиваться.

Да, и мне так вкуснее. Когда я делаю лазанью, я не буду полностью готовить макароны. Вместо этого я держу кастрюлю с кипящей водой и окунаю в нее пасту, чтобы она стала мягкой, пока собираю блюдо; макароны находятся в воде менее одной минуты. Паста, кажется, впитывает больше соуса, сохраняя вкус, но делая блюдо не так легко разваливающимся.

1

Обязательно сработает! Вы можете хранить его в холодильнике в готовом виде пару дней.

Также очень удобно замораживать в этот момент, размораживать и запекать позже.

Единственная загвоздка в том, что время выпекания, возможно, придется немного увеличить, так как вы будете работать с продуктом, охлажденным в холодильнике, а не со свежеприготовленным.

4

Несколько раз мы с женой готовили лазанью для более чем 100 человек. Как мы это сделали, мы сделали несколько полных емкостей с продуктом, а затем заморозили их для последующего использования (до двух недель). Мы видели большой успех с его техникой.

Что касается приготовления в один день, а затем приготовления на следующий (без заморозки), то это действительно хороший способ. Однако я бы не стал ждать более 48 часов, так как на этом этапе начнет происходить поломка.

Мы делали это, когда я работал в ресторане. Наполовину сваренные макароны и предварительно приготовленный мясной слой. Собрал холодным и поставил на ночь в холодильник. Приготовление на 350 f заняло около 45 минут. Он также был покрыт сначала полиэтиленовой пленкой, а затем слоем оловянной фольги. Это помогло сохранить влажность при хранении и приготовлении пищи. Кажется, это намного вкуснее.

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

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

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

1

Зарегистрируйтесь или войдите в систему

Зарегистрируйтесь с помощью Google

Зарегистрироваться через Facebook

Зарегистрируйтесь, используя адрес электронной почты и пароль

Опубликовать как гость

Электронная почта

Требуется, но не отображается

Опубликовать как гость

Электронная почта

Требуется, но не отображается

Нажимая «Опубликовать свой ответ», вы соглашаетесь с нашими условиями обслуживания, политикой конфиденциальности и политикой использования файлов cookie

Как приготовить эту пасту для лазаньи?

спросил

Изменено 2 года, 4 месяца назад

Просмотрено 4к раз

Я попробую сделать лазанью и у меня есть сомнения, как быть с макаронами. Соусы болоньезе и бешамель прекрасны (по крайней мере, я знаю, как к ним подойти).

Проблема, с которой я столкнулся, связана с этими «предварительно приготовленными» листами пасты для лазаньи (это во Франции, Barilla — очень распространенная марка макарон здесь)

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

Что означает первый значок?

Это может быть частью рецепта соуса бешамель, но в нем отсутствует все остальное (ру и т. д.).

Я должен вскипятить 0,9 л молока, вылить его в плоскую миску, затем положить соус (не могу поверить, может первый слой макарон поверх молока? — все равно не имеет большого смысла ) а потом слои бешамель и соус?

Другими словами: куда идут эти 0,9 л молока?

РЕДАКТИРОВАТЬ: на французском сайте Barilla есть раздел с рецептами, в том числе несколько лазаньи с макаронами, которые у меня есть. https://www.barilla.com/fr-fr/recettes/collezione/lasagnes-a-la-sauce-napoletana-et-legumes-grilles, например, упоминает молоко только в контексте соуса бешамель.

ПОСЛЕДНЯЯ РЕДАКТИРОВАНИЕ : Я получил сообщение от Barilla, в котором говорилось, что первая иконка — это «рецепт бешамеля»… Ну, неважно. Я сделала соус болоньезе по-своему и использовала 50 г муки + 50 г сливочного масла (ру), + 0,9л молока, текстура и количество были идеальными. Всем спасибо.

  • макароны
  • лазанья

3

Я нашел письменные инструкции по использованию этой пасты на веб-сайте Barilla. Они почти такие же, как вы интерпретировали иконические инструкции, то есть это не полный рецепт.

ПРИГОТОВЛЕНИЕ ПАСТА

  1. Варить лазанью, готовую в духовке, не нужно.

  2. Выложите содержимое упаковки прямо в форму для запекания и добавьте лазанью, соус и начинку.

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

  4. Лазанью можно приготовить за 24 часа без выпечки. Приготовьте и храните лазанью в холодильнике при температуре не выше 40°F. Поскольку лазанья будет холодной, потребуется более длительное время выпекания, примерно 60 минут при 375°F.

  5. Чтобы заморозить лазанью, мы рекомендуем не запекать ее до замораживания. Вы можете собрать лазанью в контейнере для морозильной камеры/духовки, плотно накрыть крышкой и заморозить. Когда вы будете готовы испечь лазанью, разморозьте ее в холодильнике в течение 24 часов. Выпекайте при 375 ° F и увеличьте время выпечки до 70 минут.

На отдельной странице с «Советами и рекомендациями» по приготовлению лазаньи есть инструкции по использованию предварительно приготовленных листов лазаньи:

Листы для лазаньи Barilla Oven Ready упрощают сборку этого классического блюда из макарон. Для достижения наилучших результатов мы предлагаем следующие советы по сборке лазаньи:

  • Начните с формы для выпечки размером 13 x 9 x 3 дюймов, покрытой антипригарным кулинарным спреем.
  • Выложите 1 стакан соуса на дно формы для запекания и начните выкладывать слоями.
  • Поместите 3-4 листа сырой лазаньи Barilla рядом друг с другом на дно формы. В процессе выпекания лазанья расширится до краев формы.
  • Сверху положите начинку или соус, как указано в рецепте, на каждый слой, распределяя каждый слой по краям формы, чтобы запечатать и приготовить лазанью во время выпекания.
  • Повторите этот процесс для 4-5 слоев.
  • Последний слой будет состоять из 3-4 листов сырой лазаньи Barilla, готовой к духовке, плюс оставшийся соус и сыр, если они включены в рецепт.

У них также есть вопросы и ответы:

В: Я использую красный соус собственного приготовления, в рецепте на обратной стороне коробки не указано, сколько унций соуса требуется для этого рецепта.

A: Наш рецепт требует минимум 48 унций соуса. Важно, чтобы соуса было достаточно, чтобы лазанья была влажной и насыщенной.

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

A: Мы считаем, что необходимо минимум 6-8 чашек.

Эти вопросы и ответы относятся к упаковке размером 9 унций (255 г), доступной в США. Таким образом, для вашей 500-граммовой коробки вам понадобится вдвое больше рекомендуемого количества соуса. Для красного соуса используйте не менее 48 унций (1,4 л) соуса на 255 г пасты или 96 унций (2,8 л) соуса на полную 500-граммовую коробку. Для белого соуса (соус бешамель) используйте от 1,4 до 1,9 л на половину упаковки или от 2,8 до 3,8 л на полную упаковку.

Здесь компания Barilla предлагает несколько рецептов предварительно приготовленных листов для лазаньи.

3

Barilla говорит, что их лазанья не требует варки (не вся лазанья), поэтому я могу только предположить, что они ожидают, что вы сделаете свой бешамель с 900 мл молока, а затем используете банку их соуса болоньезе в чередующихся слоях. Это немного не хватает точности, конечно.

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

Я только что просмотрела сайты и рецепты Barilla по всему миру, большинство продуктов Barilla не готовятся предварительно — это также написано на вашей коробке — предварительно готовят только их волнистую лазанью.

1

Для макарон: Вы просто кладете их в форму для запекания.

Их не нужно предварительно готовить.

Остальная часть вопроса: кажется, что значки относятся к этапам рецепта, который должен быть распечатан на коробке.

0,9 л соответствует 900 г молока, указанному в связанном рецепте.

Шаги выглядят очень минималистичным описанием рецепта.

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

10 листов лазаньи
Рагу: 400 г говяжьего фарша, 40 г нарезанного лука, 100 г нарезанной кубиками моркови, 40 г нарезанного сельдерея, 30 г томатного концентрата, 40 мл воды, 4 столовые ложки оливкового масла первого отжима, 80 г тертого сыра пармезан, 1 лавровый лист , соль и перец.
Бешамель: 0,9 л молока, 50 г сливочного масла, 50 г муки, мускатный орех, соль

  1. В кастрюле обжарить в масле лук, сельдерей, морковь, лавровый лист и мясной фарш до коричневого цвета.
  2. Добавьте томатный концентрат и воду, приправьте солью и перцем и готовьте 20 минут на среднем огне. Для простого соуса Болоньезе: после обжаривания говяжьего фарша на сковороде добавьте соус Barilla Napoletana и подогрейте.
  3. В большой кастрюле растопить сливочное масло, затем муку и все предварительно вскипяченное молоко. Взбейте смесь, добавляя соль и мускатный орех, и доведите до кипения.
  4. Смажьте форму для запекания жиром и выложите на дно половник бешамель и 4 столовые ложки рагу, затем накройте 2 листами лазаньи.
  5. Повторите предыдущий шаг 4 раза, сформировав 5 слоев. Убедитесь, что вы покрыли углы каждого листа большим количеством соуса бешамель и рагу. На каждый слой посыпьте 15 г тертого сыра Пармиджано Реджано.
  6. Выпекать в предварительно разогретой до 220°С духовке 20 минут. Выньте из духовки и оставьте на 10 минут.

Это макароны без варки, то есть вам не нужно готовить листы макарон перед тем, как положить их в духовку.

По сути, вы чередуете один слой бешамель (достаточно, чтобы покрыть дно формы для запекания), один слой (сырой) пасты и слой рагу (опять же, ровно столько, чтобы покрыть макароны). Повторяйте, пока блюдо не будет заполнено или у вас не закончатся один или несколько ингредиентов. Сверху посыпать тертым сыром и запекать 20 минут при температуре 200°C.

Конечно, рагу можно заменить любым другим томатным соусом.

Чтобы готовый продукт оставался твердым и не растекался, текстура должна быть густой/кремовой, а не жидкой.

Вам может понравится

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *