33

[LSTM]初识大名鼎鼎的Seg2Seg模型: LSTM

 5 years ago
source link: http://www.flyml.net/2018/08/12/lstm-series-first-know-lstm-seg2seg-model/?amp%3Butm_medium=referral
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

AJRjmiI.png!web LSTM 应该是这段时间应用非常广泛也非常火的一个算法了。关于其理论基础, 已经有非常好的文章了。 在这里, 笔者记录一下自己从一些实际例子来学习LSTM的路程。 不太监的话, 应该会有好几篇文章。 这是第一篇, 主要目的:

  • 从一个简单的例子来看看LSTM是如何预测下一个输出的。
  • 从这个简单的例子, 体会一下什么是Seg2Seg。

  • 大概看看, 如何使用Keras来训练一个LSTM的模型。

主要参考文章:

问题描述:学习字母表

我们经常听说LSTM可以用来训练机器人进行说话, 或者使用LSTM之类的Seg2Seg算法进行翻译。 这个命题就是一个简化版本的预测输出的命题。

假设我们有一个数据集 ABCDEFGHIJKLMNOPQRSTUVWXYZ 。 给定一个输入序列, 预测下一个字母是什么。

举个例子:

A -> B
B -> C
C -> D
# ...
 

上面的例子之中, 我们只使用了1个输入字符,预测1个输出字符。也就是我们的输入输出序列长度都是1.

注意: 我们学过字母表及其顺序, 现在是让算法学会这个顺序。 就像交小孩子学习字母表及其顺序那样。后面我们也会尝试修改 -> 左边的数值。

数据集准备

  • 首先, 我们还是要引入相应的类库, 并设定一个随机值
    import numpy
    from keras.models import Sequential
    from keras.layers import Dense
    from keras.layers import LSTM
    from keras.utils import np_utils
     
    # fix random seed for reproducibility
    numpy.random.seed(7)
     
    

  • 然后, 将字符映射成数字:

    采用大写是为了后面好看, 小写字母就忽略不计了。

    # define the raw dataset
    alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
    # create mapping of characters to integers (0-25) and the reverse
    char_to_int = dict((c, i) for i, c in enumerate(alphabet))
    int_to_char = dict((i, c) for i, c in enumerate(alphabet))
     
    
  • 第三步:尝试构建出上面1:1映射的数据集
    # prepare the dataset of input to output pairs encoded as integers
    seq_length = 1   # 注意这里, seq_length = 1
    dataX = []
    dataY = []
    for i in range(0, len(alphabet) - seq_length, 1):
    seq_in = alphabet[i:i + seq_length]
    seq_out = alphabet[i + seq_length]
    dataX.append([char_to_int[char] for char in seq_in])
    dataY.append(char_to_int[seq_out])
    print seq_in, '->', seq_out
    # 上面的程序运行的结果就是:X -> Y
    """
    A -> B
    B -> C
    ...
    """
     
    

  • 【重要】Reshape

    # reshape X to be        [samples   , time steps, features]
    X = numpy.reshape(dataX, (len(dataX), seq_length, 1))
    print(X.shape)  # (25, 1, 1)
    print(X)
    """
    array([[[ 0]],
     
         [[ 1]],
     
         [[ 2]],
         ...
    """
     
    

    一些我的理解:

    • samples 或者 len(dataX) : 数据有多少行
    • time_steps 或者 seq_length : 步长, 即每一次观察,有几个观测点。 我喜欢叫他 window_size

    • features 或者 1 : 特征。

    举另外一个例子可能更好懂一些。

    我们使用股市或者比特币价格作为输入。 每一个时刻,可能是分、时、日、周等等,会有好几种价格(成交量等其他feature暂略):

    • 开盘价
    • 收盘价
    • 最高价
    • 最低价

    如果我们只使用最高价, 那么 features == 1 , 如果我们使用多种价格或者把这一时刻的成交量也加进来, 那么 features == 4

    关于为什么要reshape成这个样子, 更详细的可以参考:

    How to Reshape Input Data for Long Short-Term Memory Networks in Keras

  • 正则化数据(normalize)

    # normalize
    X = X / float(len(alphabet))
     
    

    注意: 如果是其他的数据集,也需要进行类似的操作。 比如 MinMaxScaler

  • 因为字符是有限的, 相当于预测是26类之中的哪一个。 因此进行下面的处理方式:

    # one hot encode the output variable
    y = np_utils.to_categorical(dataY)
     
    """
    array([[ 0.,  1.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,
           0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.],
         [ 0.,  0.,  1.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,
           0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.],
         [ 0.,  0.,  0.,  1.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,
           0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.],
          ....
    """
     
    y.shape # (25, 26)
     
    

    注意: LSTM算法的输入输出需要转换成数值类型。 因此在其他命题之中, 如果原始输出不是数值, 也需要进行类似的转换。

LSTM 初体验: seq_length =1, features = 1

原文小标题: Naive LSTM for Learning One-Char to One-Char Mapping

这种是最简单朴素的方式。 feature & seq_length 都是1

训练模型

# create and fit the model
model = Sequential()
# 32: cell state size,  input_shape: 期望1+ samples
model.add(LSTM(32, input_shape=(X.shape[1], X.shape[2]))) 
model.add(Dense(y.shape[1], activation='softmax'))  # 输出多个类
model.compile(loss='categorical_crossentropy', optimizer='adam', metrics=['accuracy'])
model.fit(X, y, epochs=500, batch_size=1, verbose=2)
 

实测一下结果:

# summarize performance of the model
scores = model.evaluate(X, y, verbose=0)
print("Model Accuracy: %.2f%%" % (scores[1]*100))
 
# Output: Model Accuracy: 76.00%
 

原文将这个LSTM模型称为 Navie LSTM . 我们看看后面应该如何提高成绩。

修改: seq_length = 3, feature = 1

两处修改:

seq_length = 3
 
# reshape X to be        [samples   , time steps, features]
X = numpy.reshape(dataX, (len(dataX), 3,          1))
 

此时生成的数据集:

ABC -> D
BCD -> E
CDE -> F
DEF -> G
EFG -> H
FGH -> I
GHI -> J
 

这种方式把输入序列当成3个特征, 个人认为只是一种测试方式。

结果: 100%

比如 ABC -> D 实际上, 因为有了context信息, 所以对后面的数据 BCD->E 也是有帮助的。

修改:batch_size

Keras在每一个mini_batch之中会保存状态, 但是这一个batch结束之后, 就会reset。

基于最开始的 seq_length = 1, features=1 , 我们修改 batch_size=len(dataX)

结果: 100%

也可以通过参数强制让Keras不重置状态。

修改: 变长字符

假设Sample长下面这样, 即数据集是变长的。

# prepare the dataset of input to output pairs encoded as integers
num_inputs = 1000
max_len = 5
dataX = []
dataY = []
for i in range(num_inputs):
    start = numpy.random.randint(len(alphabet)-2)
    end = numpy.random.randint(start, min(start+max_len,len(alphabet)-1))
    sequence_in = alphabet[start:end+1]
    sequence_out = alphabet[end + 1]
    dataX.append([char_to_int[char] for char in sequence_in])
    dataY.append(char_to_int[sequence_out])
    print sequence_in, '->', sequence_out
 

生成的Sample:

PQRST -> U
W -> X
O -> P
OPQ -> R
IJKLM -> N
QRSTU -> V
ABCD -> E
X -> Y
GHIJ -> K
 

此时的处理方式:

X = pad_sequences(dataX, maxlen=max_len, dtype='float32')
 

主要是使用 pad_sequences 。 长度不足的地方会用 0 填充。

其他一些值得看看的文章:

Tensorflow实例:利用LSTM预测股票每日最高价(一)

Tensorflow实例:利用LSTM预测股票每日最高价(二)

LSTM Neural Network for Time Series Prediction

Multidimensional LSTM Networks to Predict Bitcoin Price


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK