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

Код

Видео

Сначала давайте начнем с классного трюка с Python. Напишите @property сверху метода, и вы сможете вызывать его без скобок.

@property
def test():
    return "Hello"
print(test)
OUTPUT: Hello

Чтобы построить наши данные, нам часто нужно уменьшить размерность. Одним из наиболее распространенных методов является PCA.

from sklearn.decomposition import PCA
pca = PCA(n_components=3)
data_pca = pca.fit(data).components_

PCA уменьшает количество измерений, комбинируя наиболее похожие измерения. Размеры результатов настолько различны, насколько это возможно. Понимать это не очень важно, потому что за нас это делает scikit-learn.

плотный слой = линейный слой

Джереми показал из какой-то статьи, как использование матрицы встраивания (EE) поможет всем разным типам моделей. Он объяснил, что если вы сделаете свои данные для использования матрицы встраивания, их можно будет легко передать любому с разными типами моделей, и все они получат довольно хорошие результаты. В этом листе ниже лучше.

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

Удаление столбцов не сильно помогает в глубоком обучении, поэтому лучше просто дать модели возможность выбирать, что удалить, а что оставить.

[1:10:07]

Рекуррентная нейронная сеть (RNN)

RNN похожа на обычную нейронную сеть, но она построена таким образом, что может запоминать старые вещи. Например, если в предложении в начале есть «мужчина», то RNN может позже понять, что это «он», а не «она» или «оно».

Код этой диаграммы:

Сначала вводим данные

PATH = 'data/nietzsche/'
get_data("https://s3.amazonaws.com/text-dataset/nietzsche.txt",f'{PATH}'nietzsche.txt)
text = open(f'{PATH}nietzsche.txt').read()
print('corpus length:',len(text))
OUTPUT: 600893

chars = sorted(list(set(text)))
vocab_size = len(chars)+1
print('total chars:',vocab_size)
OUTPUT: 85

Сопоставьте каждый символ с уникальным идентификатором (1-я строка) и каждый уникальный идентификатор с символом (2-я строка).

char_indices = dict((c, i) for i, c in enumerate(chars))
indices_char = dict((i, c) for i, c in enumerate(chars))

Далее мы преобразуем все символы в тексте в их индекс

idx = [char_indices[c] for c in text]
idx[:5]
OUTPUT: [40, 42, 29, 30, 25]

Мы пытаемся предсказать 4-й символ, используя 3 более ранних символа.

cs = 8
c_in_dat = [[idx[i+j] for i in range(cs)] for j in range(len(idx)-
                         cs-1)]
c_out_dat = [idx[j+cs] for j in range(len(idx)-cs-1)]

Входы

xs = np.stack(c_in_dat,axis=0)

Выходы

y = np.stack(c_out_dat)

Габаритные размеры

x.shape
OUTPUT: (600884,8)

Далее мы создадим эту модель выше и обучим ее.

n_hidden = 256
n_fac = 42
val_idx = get_cv_idxs(len(idx)-cs-1)
md = ColumnarModelData.from_arrays('.',val_idx,xs,y,bs=512)
class Char3Model(nn.Module):
    def __init__(self,vocab_size,n_fac):
        super().__init__()
        self.e = nn.Embedding(vocab_size,n_fac)
        
        self.l_in = nn.Linear(n_fac+n_hidden, n_hidden)
        self.l_hidden = nn.Linear(n_hidden, n_hidden)
        self.l_out = nn.Linear(n_hidden, vocab_size)
    def forward(self,c1,c2,c3):
        bs = cs[0].size(0)
        h = V(torch.zeros(bs,n_hidden).cuda())
        for c in cs:
            inp = torch.cat((h,self.e(c)),1)
            inp = F.relu(self.l_in(inp))
            h = F.tanh(self.l_hidden(inp))
        return F.log_softmax(self.l_out(h))

m = Char3Model(vocab_size,n_fac).cuda()
it = iter(md.trn_dl)
*xs,yt = nex(it)
t = m(*V(xs))
opt = optim.Adam(m.parameters(),1e-2)
fit(m,md,1,opt,F.nll_loss)
set_lrs(opt,0.001)
fit(m,md,1,opt,F.nll_loss)

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

def get_next(inp):
    idxs = T(np.array([char_indices[c] for c in inp]))
    p = m(*VV(idxs))
    i = np.argmax(to_np(p))
    return chars[i]
get_next('y. ')
OUTPUT: 'T'
get_next('and')
OUTPUT: ' '
get_next('part of')
OUTPUT: 't'

[1:48:50]

То же самое с использованием PyTorch

class CharRNN(nn.Module):
    def __init__(self,vocab_size,n_fac):
        super().__init__()
        self.e = nn.Embedding(vocab_size,n_fac)
        self.rnn = nn.RNN(n_fac,n_hidden)
        self.l_out = nn.Linear(n_hidden, vocab_size)
    def forward(self, *cs):
        bs = cs[0].size(0)
        h = V(torch.zeros(1,bs,n_hidden))
        inp = self.e(torch.stack(cs))
        output,h = self.rnn(inp,h)
        return F.log_softmax(self.l_out(outp[-1]))
m = CharRnn(vocab_size,n_fac).cuda()
opt = optim.Adam(m.parameters(),1e-3)
it = iter(md.trn_dl)
*xs,yt = next(it)
t = m.e(V(torch.stack(xs)))
ht = V(torch.zeros(1,512,n_hidden))
outp, hn = m.rnn(t,ht)
t = m(*V(xs))
fit(m,md,1,opt,F.nll_loss)

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

m.rnn.weight_hh_10.data.copy_(torch.eye(n_hidden))

~ Ланкинен