Вопрос 1: Можно ли использовать сверточные сети для классификации текстов? Если нет обоснуйте :D, если да то как? как решить проблему с произвольной длинной входа?
<Ответ> Да, есть 2 подхода.
Вопрос 2: Чем LSTM лучше/хуже чем обычная RNN?
<Ответ> В обычной RNN градиент может "взрываться" или наоборот "затухать", что очевидно плохо сказывается на обучении. В LSTM мы навешиваем контроль памяти, этим контролируя затухание градиента, например с помощью forget_gate (обнуления градиентов от некоторых предыдущих состояний) и другая работа со скрытым состоянием. При этом остается проблема "взрыва" градиента (которую можно частично решать grad_clipping-ом)
Вопрос 3: Выпишите производную $\frac{d c_{n+1}}{d c_{k}}$ для LSTM http://colah.github.io/posts/2015-08-Understanding-LSTMs/, объясните формулу, когда производная затухает, когда взрывается?
<Ответ> $\frac{dc_{n+1}}{dc_k} = \frac{dc_{n+1}}{dc_n} \cdot \frac{dc_n}{dc_k} = diag(\prod_{i=k+1}^{n+1} f_i)$, diag(вектора) - матрица с координатами вектора как диагональные элементы матрицы. Если $\frac{dc_{n+1}}{dc_k} > 1$ (имеется в виду диагональный элемент), то градиент "взрывается" и это плохо, если $<1$, то затухает, но это мы контролируем через forget_gate в LSTM.
Вопрос 4: Зачем нужен TBPTT почему BPTT плох?
<Ответ> BPTT плох тем, что на большом удалении градиент сильно затухает и практически не влияет ни на что. Поэтому, чтобы не тратить время на подсчет там, где это бесполезно, можно "обрезать" распространение градиента, что и делает TBPTT.
Вопрос 5: Как комбинировать рекуррентные и сверточные сети, а главное зачем? Приведите несколько примеров реальных задач.
<Ответ> Выход сверточных сетей, если убрать последний слой (подсчет вероятностей в softmax), дает векторное описание картинок, которое мы используем как начальное скрытое состояние для рекурентной сети. Соответственно применяем комбинацию этих сетей для image captioning и похожих задач, например, генерация речи, подпись видео.
Вопрос 6: Объясните интуицию выбора размера эмбединг слоя? почему это опасное место?
<Ответ> Если размер Embedding-а меньше нужного, то мы не сможем все закодировать (т.е. потеряем часть информации), что, очевидно, плохо и приводит к недообучению. С другой стороны избыточный размер приводит к тому, что мы тратим лишнюю память, а кроме того добавляем неинформативные фичи(так как все уже закодировано) и приводит к переобучению.
ars.ashuha@gmail.com
, Александр ПанинIn this seminar you'll be going through the image captioning pipeline. It can help u https://ars-ashuha.ru/slides/2016.11.11_ImageCaptioning/image_captionong.pdf
To begin with, let us download the dataset of image features from a pre-trained GoogleNet.
!wget https://www.dropbox.com/s/3hj16b0fj6yw7cc/data.tar.gz?dl=1 -O data.tar.gz !tar -xvzf data.tar.gz
In [1]:
%%time
# Read Dataset
import numpy as np
import pickle
img_codes = np.load("data/image_codes.npy")
captions = pickle.load(open('data/caption_tokens.pcl', 'rb'))
In [2]:
print "each image code is a 1000-unit vector:", img_codes.shape
print img_codes[0,:10]
print '\n\n'
print "for each image there are 5-7 descriptions, e.g.:\n"
print '\n'.join(captions[0])
In [3]:
#split descriptions into tokens
for img_i in range(len(captions)):
for caption_i in range(len(captions[img_i])):
sentence = captions[img_i][caption_i]
captions[img_i][caption_i] = ["#START#"]+sentence.split(' ')+["#END#"]
In [4]:
# Build a Vocabulary
############# TO CODE IT BY YOURSELF ##################
from collections import Counter
word_counts = Counter(word for pic in captions for line in pic for word in line[1:-1]) # <here should be dict word:number of entrances>
# not to double START and END
vocab = ['#UNK#', '#START#', '#END#']
vocab += [k for k, v in word_counts.items() if v >= 5]
n_tokens = len(vocab)
assert 10000 <= n_tokens <= 10500
word_to_index = {w: i for i, w in enumerate(vocab)}
In [5]:
PAD_ix = -1
UNK_ix = vocab.index('#UNK#')
def as_matrix(sequences,max_len=None):
max_len = max_len or max(map(len,sequences))
matrix = np.zeros((len(sequences),max_len),dtype='int32')+PAD_ix
for i,seq in enumerate(sequences):
row_ix = [word_to_index.get(word,UNK_ix) for word in seq[:max_len]]
matrix[i,:len(row_ix)] = row_ix
return matrix
In [6]:
#try it out on several descriptions of a random image
as_matrix(captions[1337])
Out[6]:
In [7]:
# network shapes.
CNN_FEATURE_SIZE = img_codes.shape[1]
EMBED_SIZE = 128 * 2 #pls change me if u want
LSTM_UNITS = 700 #pls change me if u want
In [8]:
import theano
import lasagne
import theano.tensor as T
from lasagne.layers import *
In [9]:
# Input Variable
sentences = T.imatrix()# [batch_size x time] of word ids
image_vectors = T.matrix() # [batch size x unit] of CNN image features
sentence_mask = T.neq(sentences, PAD_ix)
In [10]:
#network inputs
l_words = InputLayer((None, None), sentences)
l_mask = InputLayer((None, None), sentence_mask)
#embeddings for words
############# TO CODE IT BY YOURSELF ##################
l_word_embeddings = EmbeddingLayer(l_words, n_tokens, EMBED_SIZE)
In [11]:
# input layer for image features
l_image_features = InputLayer((None, CNN_FEATURE_SIZE), image_vectors)
############# TO CODE IT BY YOURSELF ##################
#convert 1000 image features from googlenet to whatever LSTM_UNITS you have set
#it's also a good idea to add some dropout here and there
l_image_features_small = DropoutLayer(l_image_features)
l_image_features_small = DenseLayer(l_image_features_small, LSTM_UNITS)
assert l_image_features_small.output_shape == (None, LSTM_UNITS)
In [12]:
############# TO CODE IT BY YOURSELF ##################
# Concatinate image features and word embedings in one sequence
decoder = LSTMLayer(l_word_embeddings,
num_units=LSTM_UNITS,
cell_init=l_image_features_small,
mask_input=l_mask,
grad_clipping=1e30)
In [13]:
# Decoding of rnn hiden states
from broadcast import BroadcastLayer,UnbroadcastLayer
#apply whatever comes next to each tick of each example in a batch. Equivalent to 2 reshapes
broadcast_decoder_ticks = BroadcastLayer(decoder, (0, 1))
print "broadcasted decoder shape = ",broadcast_decoder_ticks.output_shape
predicted_probabilities_each_tick = DenseLayer(
broadcast_decoder_ticks,n_tokens, nonlinearity=lasagne.nonlinearities.softmax)
#un-broadcast back into (batch,tick,probabilities)
predicted_probabilities = UnbroadcastLayer(
predicted_probabilities_each_tick, broadcast_layer=broadcast_decoder_ticks)
print "output shape = ", predicted_probabilities.output_shape
#remove if you know what you're doing (e.g. 1d convolutions or fixed shape)
assert predicted_probabilities.output_shape == (None, None, 10371)
In [14]:
next_word_probas = get_output(predicted_probabilities)
reference_answers = sentences[:,1:]
output_mask = sentence_mask[:,1:]
#write symbolic loss function to train NN for
loss = lasagne.objectives.categorical_crossentropy(
next_word_probas[:, :-1].reshape((-1, n_tokens)),
reference_answers.reshape((-1,))
).reshape(reference_answers.shape)
############# TO CODE IT BY YOURSELF ##################
loss = (loss * output_mask).sum() / output_mask.sum()
In [15]:
#trainable NN weights
############# TO CODE IT BY YOURSELF ##################
weights = get_all_params(predicted_probabilities)
updates = lasagne.updates.adam(loss, weights)
In [16]:
#compile a function that takes input sentence and image mask, outputs loss and updates weights
#please not that your functions must accept image features as FIRST param and sentences as second one
############# TO CODE IT BY YOURSELF ##################
train_step = theano.function([image_vectors, sentences], loss, updates=updates)
val_step = theano.function([image_vectors, sentences], loss)
In [17]:
captions = np.array(captions)
In [18]:
from random import choice
def generate_batch(images,captions,batch_size,max_caption_len=None):
#sample random numbers for image/caption indicies
random_image_ix = np.random.randint(0, len(images), size=batch_size)
#get images
batch_images = images[random_image_ix]
#5-7 captions for each image
captions_for_batch_images = captions[random_image_ix]
#pick 1 from 5-7 captions for each image
batch_captions = map(choice, captions_for_batch_images)
#convert to matrix
batch_captions_ix = as_matrix(batch_captions,max_len=max_caption_len)
return batch_images, batch_captions_ix
In [19]:
generate_batch(img_codes,captions, 3)
Out[19]:
In [20]:
batch_size = 200 #50 #adjust me
n_epochs = 100 #adjust me
n_batches_per_epoch = 50 #adjust me
n_validation_batches = 5 #how many batches are used for validation after each epoch
In [21]:
from tqdm import tqdm
for epoch in range(n_epochs):
train_loss=0
for _ in tqdm(range(n_batches_per_epoch)):
train_loss += train_step(*generate_batch(img_codes,captions,batch_size))
train_loss /= n_batches_per_epoch
val_loss=0
for _ in range(n_validation_batches):
val_loss += val_step(*generate_batch(img_codes,captions,batch_size))
val_loss /= n_validation_batches
print('\nEpoch: {}, train loss: {}, val loss: {}'.format(epoch, train_loss, val_loss))
print("Finish :)")
In [22]:
from tqdm import tqdm
for epoch in range(n_epochs):
train_loss=0
for _ in tqdm(range(n_batches_per_epoch)):
train_loss += train_step(*generate_batch(img_codes,captions,batch_size))
train_loss /= n_batches_per_epoch
val_loss=0
for _ in range(n_validation_batches):
val_loss += val_step(*generate_batch(img_codes,captions,batch_size))
val_loss /= n_validation_batches
print('\nEpoch: {}, train loss: {}, val loss: {}'.format(epoch, train_loss, val_loss))
print("Finish :)")
In [28]:
#the same kind you did last week, but a bit smaller
from pretrained_lenet import build_model,preprocess,MEAN_VALUES
# build googlenet
lenet = build_model()
#load weights
lenet_weights = pickle.load(open('data/blvc_googlenet.pkl'))['param values']
set_all_param_values(lenet["prob"], lenet_weights)
#compile get_features
cnn_input_var = lenet['input'].input_var
cnn_feature_layer = lenet['loss3/classifier']
get_cnn_features = theano.function([cnn_input_var], lasagne.layers.get_output(cnn_feature_layer))
In [29]:
from matplotlib import pyplot as plt
%matplotlib inline
#sample image
img = plt.imread('data/Dog-and-Cat.jpg')
img = preprocess(img)
In [30]:
#deprocess and show, one line :)
from pretrained_lenet import MEAN_VALUES
plt.imshow(np.transpose((img[0] + MEAN_VALUES)[::-1],[1,2,0]).astype('uint8'))
Out[30]:
In [37]:
last_word_probas_det = get_output(predicted_probabilities,deterministic=False)[:,-1]
get_probs = theano.function([image_vectors,sentences], last_word_probas_det)
#this is exactly the generation function from week5 classwork,
#except now we condition on image features instead of words
def generate_caption(image,caption_prefix = ("START",),t=1,sample=True,max_len=100):
image_features = get_cnn_features(image)
caption = list(caption_prefix)
for _ in range(max_len):
next_word_probs = get_probs(image_features,as_matrix([caption]) ).ravel()
#apply temperature
next_word_probs = next_word_probs**t / np.sum(next_word_probs**t)
if sample:
next_word = np.random.choice(vocab,p=next_word_probs)
else:
next_word = vocab[np.argmax(next_word_probs)]
caption.append(next_word)
if next_word=="#END#":
break
return caption
In [38]:
for i in range(10):
print ' '.join(generate_caption(img,t=1.)[1:-1])
In [ ]: