Я использую тензорный поток с керасами и пытаюсь построить простой двухслойный RNN с ячейками LSTM (для моделирования языка, прогнозирования следующего слова).Я использую набор данных PTB и пытаюсь реализовать сети из "Регулярной нейронной регуляризации (Embedding -> LSTM1 -> LSTM2 -> Dense). К сожалению, у меня странные результаты,первая эпоха выглядит великолепно, потери снижаются с ~ 9 до ~ 6,9 (категорическая перекрестная энтропия), но она застревает там навсегда (даже при том, что я уменьшаю свою скорость обучения). В качестве теста я хотел убедиться, что я могу превзойтималенький текст с похожей архитектурой, но я получаю те же результаты! Я использую размер пакета 20 и 20 шагов в каждом образце. Я на 100% уверен в своей функции batchGenerator ...
Можно ли переодеться? Что мне не хватает?
мой код:
tempSet = batchGenerator(text = testSet[:10000], batchSize = batchSize,timeStepsPerBatch = timeStepsPerBatch,vocabSize = vocabSize)
model = Sequential()
model.add(Embedding(vocabSize, 200, input_length=20,batch_input_shape = (20,20)))
model.add(LSTM(200,return_sequences=True,stateful = True))
model.add(LSTM(200,return_sequences=True,stateful = True))
model.add(TimeDistributed(Dense(vocabSize)))
model.add(Activation('softmax'))
model.compile(loss='categorical_crossentropy',
optimizer='adam', metrics=['categorical_accuracy'])
# add reset call back after each epoch, add preplixty mdss
modelHistory = model.fit_generator(tempSet.generate(),
steps_per_epoch=tempSet.stepPerEpoch, epochs=100, verbose=1,
callbacks=[resetStateAtEpochEndCall],
validation_data=None, validation_steps=None)
Результаты: Epoch 1/100 24/24 [==============================] - 11 с 471 мс / шаг - потеря: 8,1857 - категориальная точность: 0,0422 эпоха 2/100 24/24 [==============================] - 4s 156 мс / шаг - потеря: 6,2512 - категориальная_точность: 0,0536 эпоха 3/100 24/24 [==============================] - 4s 155мс / шаг - потеря: 6.1297 - категориальная_точность: 0.0504 Epoch 4/100 24/24 [====================================]- 4s 154 мс / шаг - потеря: 6.0974 - категорическая точность: 0.0497 Epoch 5/100 24/24 [====================================] - 4 с 153 мс / шаг - потеря: 6,0877 - категориальная_точность: 0,0497 Эпоха 6/100 24/24 [=====================================] - 4 с 153 мс / шаг - потеря: 6,0828 - категориальная_точность: 0,0503 Epoch 7/100 24/24 [==============================] - 4 с 153 мс / шаг - потеря: 6,0800 - категориальная_точность: 0,0505 эпоха 8/100 24/24 [=====================================] - 4s 152 мс / шаг - потеря: 6,0781 - категориальная точность: 0,0505 Epoch 9/100 24/24 [===============================] - 4 с 152 мс / шаг - потеря: 6,0766 - категориальная точность: 0,0506 эпоха 10/100 24/24 [==============================] - 4 с 153 мс / шаг - потеря: 6,0755 - категориальная точность: 0,0506.,,24/24 [====================================] - 4 с 149 мс / шаг - потеря: 6.0477 - категориальная_точность: 0,0504 эпоха 98/ 100 24/24 [====================================] - 4 с 150 мс / шаг - потеря: 6,0470 - категориальная точность: 0,0501Epoch 99/100 24/24 [====================================] - 4s 150 мс / шаг - потеря: 6.0483 - категорическая точность: 0.0498 Epoch 100/100 24/24 [====================================] - 4s 149мс / шаг - потеря: 6.0471- категорическая точность: 0,0498
class batchGenerator:
def __init__(self, text, batchSize = 20,timeStepsPerBatch = 35,vocabSize = 9999):
self.text = text
self.textLen = len(text)
self.batchSize = batchSize
self.segLen = self.textLen//self.batchSize # we'll divide the input text into segments,
# each segment will be used to generate a sample inside a mini-batch
# this way we can use the last hidden state of the RNN as an input to the next mini batch
self.cursor = [ind*self.segLen for ind in range(self.batchSize)]
self.timeStepsPerBatch = timeStepsPerBatch
self.vocabSize = vocabSize
self.stepPerEpoch = (self.segLen - 1)//self.timeStepsPerBatch
print('Number of steps per epoch',self.stepPerEpoch)
self.iterInd = 0;
# alert for misdividing the data
lostDataOutOfSeg = self.textLen - self.segLen*self.batchSize
if(lostDataOutOfSeg > 0):
print('Amount of words lost because text didnt dived nicely to %d segments is %d Which is %.2f%% from the total data'
%(self.batchSize,lostDataOutOfSeg,((lostDataOutOfSeg/self.textLen)*100)))
lostDataInSeg = (self.segLen -1 - ((self.segLen - 1)//self.timeStepsPerBatch)*self.timeStepsPerBatch)*self.batchSize
if(lostDataInSeg > 0):
print('Amount of words lost because segment didnt dived nicely to %d time steps is: %d Which is %.2f%% from the total data'
%(self.timeStepsPerBatch,lostDataInSeg,(lostDataInSeg/self.textLen)*100))
if(lostDataOutOfSeg + lostDataInSeg > 0):
print('Total lost data : %d which is %.2f%% from the total data'
%((lostDataOutOfSeg + lostDataInSeg),(lostDataOutOfSeg + lostDataInSeg)*100/self.textLen))
def generate(self):
# outputs a mini batch of data (x,y)
# x - tensor of length batchSize. Each entrance in x is a (partial) sentence (timeStepsPerBatch words) from a segment
# y - is the same as x, except it is shifted 1 word (targets)
while(True): # genrator
self.iterInd = self.iterInd + 1;
#print(self.iterInd)
x = np.zeros((self.batchSize,self.timeStepsPerBatch),dtype = np.int32)
y = np.zeros((self.batchSize,self.timeStepsPerBatch,self.vocabSize),dtype = np.int32)
# check if can take full timeStepsPerBatch at each segment
if(self.cursor[0] + self.timeStepsPerBatch + 1> self.segLen): #TODO: double check conidtion
# end of an epoch, reset cursors
#print('End of epoch, cursor reset. iter num ',self.iterInd)
self.cursor = [ind*self.segLen for ind in range(self.batchSize)]
for i in range(self.batchSize):
x[i,:] = self.text[self.cursor[i]:(self.cursor[i] + self.timeStepsPerBatch)]
y_id = self.text[(self.cursor[i]+1):(self.cursor[i] + self.timeStepsPerBatch+1)]
y[i,:,:] = tf.keras.utils.to_categorical(y_id,num_classes = self.vocabSize) # transform to 1-hot encoding
# update cursor
self.cursor[i] = self.cursor[i] + self.timeStepsPerBatch
yield x,y