深度学习速成版04---RNN及LSTM
时间:2022-10-30 18:30:00
RNN模型
RNN(Recurrent Neural Network), 中文称循环神经网络, 它通常以序列数据为输入, 通过网络内部的结构设计,有效地捕捉序列之间的关系特征, 一般以序列的形式输出.
- RNN单层网络结构:
以时间步对RNN展开后的单层网络结构:
RNN循环机制使模型隐层一时产生的结果, 它可以作为当前时间步输入的一部分正常输入外,当前时间步输入还包括上一步的隐藏输出),影响当前时间步输出RNN因为RNN结构能够很好利用序列之间的关系, 因此,对自然界具有连续性的输入序列, 如人类语言, 语音等处理得很好, 广泛应用于NLP各个领域的任务, 如文本分类, 情感分析, 意图识别, 机器翻译等
以用户意图识别为例进行简单分析:
第一步: 用户输入了"What time is it ?", 我们首先需要对它进行基本的分词, 因为RNN按顺序工作, 每次只收到一个单词处理.
第二步: 首先将单词"What"输送给RNN, 它将产生一个输出O1.
第三步: 继续将单词"time"输送给RNN, 但此时RNN不仅仅利用"time"来产生输出O2, 它还将用于上一层的隐层输出O1作为输入信息.
第四步: 重复此步骤, 直到处理完所有的单词.
第五步: 最后,输出最终隐层O5.处理分析用户意图.
RNN模型分类:
在这里,我们将从两个角度进行对立RNN模型分类. 第一个角度是输入和输出的结构, 第二个角度是RNN的内部构造.
按输入输出结构进行分类:
N vs N - RNN
N vs 1 - RNN
1 vs N - RNN
N vs M - RNN
按照RNN内部结构分类:
传统RNN
LSTM
Bi-LSTM
GRU
Bi-GRU
-
N vs N - RNN:
它是RNN最基本的结构形式, 最大的特点是: 输入输出序列等长. 由于存在这种限制, 使其适用范围较小, 合辙诗句可用于生成等长度 -
N vs 1 - RNN:
有时我们需要处理的问题输入是一个序列,要求输出是一个单独的值,而不是一个序列。我们应该如何建模它?我们只需要在最后一个隐藏的输出h上进行线性大多数情况下,为了更好地澄清结果, 还要使用sigmoid或者softmax进行处理. 这种结构经常用于文本分类
1 vs N - RNN:
如果输入不是序列,输出是序列怎么处理?我们最常用的方法之一是使输入作用于每个输出. 该结构可用于将图片生成文本任务等.
N vs M - RNN:
这是一种无限输入输出长度RNN结构, 它由编码器和解码器组成, 两者的内部结构都是某一类的RNN, 它也被称为seq2seq架构. 首先通过编码器输入数据, 最终输出隐含变量c, 之后最常用的方法是在解码器解码的每一步都使用这个隐含变量c, 确保输入信息的有效利用.
seq2seq机器翻译最早提出架构, 由于其输入输出不受限制,现在也是应用最广泛的RNN模型结构. 机器翻译, 阅读理解, 文本摘要等许多领域都有很多应用实践.
传统RNN的内部结构图:
结构解释图:
内部结构分析:
我们专注于中间的方块, 它的输入有两部分, 分别是h(t-1)以及x(t), 代表上一步的隐层输出, 此时间步输入, 它们进入RNN结构体后, 会"融合"到一起, 根据结构解释,我们可以看到这种融合, 拼接两者, 形成新的张量[x(t), h(t-1)], 新的张量将通过全连接层(线性层), 该层使用tanh作为激活函数, 最后得到时间步输出h(t), 它将作为下一步的输入和x(t 1)一起进入结构体. 以此类推.
- 演示内部结构工艺:
内部计算公式根据结构分析得出:
激活函数tanh的作用:
帮助调整流经网络的值, tanh函数将值压缩在-1和1之间.
>>> import torch >>> import torch.nn as nn >>> rnn = nn.RNN(5, 6, 1) >>> input = torch.randn(1, 3, 5) >>> h0 = torch.randn(1, 3, 6) >>> output, hn = rnn(input, h0) >>> output tensor([[[ 0.4282, -0.8475, -0.0685, -0.4601, -0.8357, 0.1252],
[ 0.5758, -0.2823, 0.4822, -0.4485, -0.7362, 0.0084],
[ 0.9224, -0.7479, -0.3682, -0.5662, -0.9637, 0.4938]]],
grad_fn=<StackBackward>)
>>> hn
tensor([[[ 0.4282, -0.8475, -0.0685, -0.4601, -0.8357, 0.1252],
[ 0.5758, -0.2823, 0.4822, -0.4485, -0.7362, 0.0084],
[ 0.9224, -0.7479, -0.3682, -0.5662, -0.9637, 0.4938]]],
grad_fn=<StackBackward>)
传统RNN的优势:
由于内部结构简单, 对计算资源要求低, 相比之后我们要学习的RNN变体:LSTM和GRU模型参数总量少了很多, 在短序列任务上性能和效果都表现优异.
传统RNN的缺点:
传统RNN在解决长序列之间的关联时, 通过实践,证明经典RNN表现很差, 原因是在进行反向传播的时候, 过长的序列导致梯度的计算异常, 发生梯度消失或爆炸.
什么是梯度消失或爆炸呢?
根据反向传播算法和链式法则, 梯度的计算可以简化为以下公式:
其中sigmoid的导数值域是固定的, 在[0, 0.25]之间, 而一旦公式中的w也小于1, 那么通过这样的公式连乘后, 最终的梯度就会变得非常非常小, 这种现象称作梯度消失. 反之, 如果我们人为的增大w的值, 使其大于1, 那么连乘够就可能造成梯度过大, 称作梯度爆炸.
LSTM模型
LSTM(Long Short-Term Memory)也称长短时记忆结构, 它是传统RNN的变体, 与经典RNN相比能够有效捕捉长序列之间的语义关联, 缓解梯度消失或爆炸现象. 同时LSTM的结构更复杂, 它的核心结构可以分为四个部分去解析:
- 遗忘门
- 输入门
- 细胞状态
- 输出门
LSTM的内部结构图:
结构解释图:
遗忘门部分结构图与计算公式:
遗忘门结构分析:
与传统RNN的内部结构计算非常相似, 首先将当前时间步输入x(t)与上一个时间步隐含状态h(t-1)拼接, 得到[x(t), h(t-1)], 然后通过一个全连接层做变换, 最后通过sigmoid函数进行激活得到f(t), 我们可以将f(t)看作是门值, 好比一扇门开合的大小程度, 门值都将作用在通过该扇门的张量, 遗忘门门值将作用的上一层的细胞状态上, 代表遗忘过去的多少信息, 又因为遗忘门门值是由x(t), h(t-1)计算得来的, 因此整个公式意味着根据当前时间步输入和上一个时间步隐含状态h(t-1)来决定遗忘多少上一层的细胞状态所携带的过往信息.
遗忘门内部结构过程演示:
激活函数sigmiod的作用:
用于帮助调节流经网络的值, sigmoid函数将值压缩在0和1之间.
输入门部分结构图与计算公式:
输入门结构分析:
我们看到输入门的计算公式有两个, 第一个就是产生输入门门值的公式, 它和遗忘门公式几乎相同, 区别只是在于它们之后要作用的目标上. 这个公式意味着输入信息有多少需要进行过滤. 输入门的第二个公式是与传统RNN的内部结构计算相同. 对于LSTM来讲, 它得到的是当前的细胞状态, 而不是像经典RNN一样得到的是隐含状态.
输入门内部结构过程演示:
细胞状态更新分析:
细胞更新的结构与计算公式非常容易理解, 这里没有全连接层, 只是将刚刚得到的遗忘门门值与上一个时间步得到的C(t-1)相乘, 再加上输入门门值与当前时间步得到的未更新C(t)相乘的结果. 最终得到更新后的C(t)作为下一个时间步输入的一部分. 整个细胞状态更新过程就是对遗忘门和输入门的应用.
细胞状态更新过程演示:
输出门部分结构图与计算公式:
输出门结构分析:
输出门部分的公式也是两个, 第一个即是计算输出门的门值, 它和遗忘门,输入门计算方式相同. 第二个即是使用这个门值产生隐含状态h(t), 他将作用在更新后的细胞状态C(t)上, 并做tanh激活, 最终得到h(t)作为下一时间步输入的一部分. 整个输出门的过程, 就是为了产生隐含状态h(t).
输出门内部结构过程演示:
什么是Bi-LSTM ?
Bi-LSTM即双向LSTM, 它没有改变LSTM本身任何的内部结构, 只是将LSTM应用两次且方向不同, 再将两次得到的LSTM结果进行拼接作为最终输出.
Bi-LSTM结构分析:
我们看到图中对"我爱中国"这句话或者叫这个输入序列, 进行了从左到右和从右到左两次LSTM处理, 将得到的结果张量进行了拼接作为最终输出. 这种结构能够捕捉语言语法中一些特定的前置或后置特征, 增强语义关联,但是模型参数和计算复杂度也随之增加了一倍, 一般需要对语料和计算资源进行评估后决定是否使用该结构.
-
Pytorch中LSTM工具的使用:
位置: 在torch.nn工具包之中, 通过torch.nn.LSTM可调用. -
nn.LSTM类初始化主要参数解释:
input_size: 输入张量x中特征维度的大小.
hidden_size: 隐层张量h中特征维度的大小.
num_layers: 隐含层的数量.
nonlinearity: 激活函数的选择, 默认是tanh.
bidirectional: 是否选择使用双向LSTM, 如果为True, 则使用; 默认不使用. -
nn.LSTM类实例化对象主要参数解释:
input: 输入张量x.
h0: 初始化的隐层张量h.
c0: 初始化的细胞状态张量c.
>>> import torch.nn as nn
>>> import torch
>>> rnn = nn.LSTM(5, 6, 2)
>>> input = torch.randn(1, 3, 5)
>>> h0 = torch.randn(2, 3, 6)
>>> c0 = torch.randn(2, 3, 6)
>>> output, (hn, cn) = rnn(input, (h0, c0))
>>> output
tensor([[[ 0.0447, -0.0335, 0.1454, 0.0438, 0.0865, 0.0416],
[ 0.0105, 0.1923, 0.5507, -0.1742, 0.1569, -0.0548],
[-0.1186, 0.1835, -0.0022, -0.1388, -0.0877, -0.4007]]],
grad_fn=<StackBackward>)
>>> hn
tensor([[[ 0.4647, -0.2364, 0.0645, -0.3996, -0.0500, -0.0152],
[ 0.3852, 0.0704, 0.2103, -0.2524, 0.0243, 0.0477],
[ 0.2571, 0.0608, 0.2322, 0.1815, -0.0513, -0.0291]],
[[ 0.0447, -0.0335, 0.1454, 0.0438, 0.0865, 0.0416],
[ 0.0105, 0.1923, 0.5507, -0.1742, 0.1569, -0.0548],
[-0.1186, 0.1835, -0.0022, -0.1388, -0.0877, -0.4007]]],
grad_fn=<StackBackward>)
>>> cn
tensor([[[ 0.8083, -0.5500, 0.1009, -0.5806, -0.0668, -0.1161],
[ 0.7438, 0.0957, 0.5509, -0.7725, 0.0824, 0.0626],
[ 0.3131, 0.0920, 0.8359, 0.9187, -0.4826, -0.0717]],
[[ 0.1240, -0.0526, 0.3035, 0.1099, 0.5915, 0.0828],
[ 0.0203, 0.8367, 0.9832, -0.4454, 0.3917, -0.1983],
[-0.2976, 0.7764, -0.0074, -0.1965, -0.1343, -0.6683]]],
grad_fn=<StackBackward>)
LSTM优势:
LSTM的门结构能够有效减缓长序列问题中可能出现的梯度消失或爆炸, 虽然并不能杜绝这种现象, 但在更长的序列问题上表现优于传统RNN.
LSTM缺点:
由于内部结构相对较复杂, 因此训练效率在同等算力下较传统RNN低很多.
GRU模型
GRU(Gated Recurrent Unit)也称门控循环单元结构, 它也是传统RNN的变体, 同LSTM一样能够有效捕捉长序列之间的语义关联, 缓解梯度消失或爆炸现象. 同时它的结构和计算要比LSTM更简单, 它的核心结构可以分为两个部分去解析:
- 更新门
- 重置门
GRU的内部结构图和计算公式:
结构解释图:
GRU的更新门和重置门结构图:
内部结构分析:
和之前分析过的LSTM中的门控一样, 首先计算更新门和重置门的门值, 分别是z(t)和r(t), 计算方法就是使用X(t)与h(t-1)拼接进行线性变换, 再经过sigmoid激活. 之后更新门门值作用在了h(t-1)上, 代表控制上一时间步传来的信息有多少可以被利用. 接着就是使用这个更新后的h(t-1)进行基本的RNN计算, 即与x(t)拼接进行线性变化, 经过tanh激活, 得到新的h(t). 最后重置门的门值会作用在新的h(t),而1-门值会作用在h(t-1)上, 随后将两者的结果相加, 得到最终的隐含状态输出h(t), 这个过程意味着重置门有能力重置之前所有的计算, 当门值趋于1时, 输出就是新的h(t), 而当门值趋于0时, 输出就是上一时间步的h(t-1).
Bi-GRU与Bi-LSTM的逻辑相同, 都是不改变其内部结构, 而是将模型应用两次且方向不同, 再将两次得到的LSTM结果进行拼接作为最终输出. 具体参见上小节中的Bi-LSTM.
Pytorch中GRU工具的使用:
位置: 在torch.nn工具包之中, 通过torch.nn.GRU可调用.
nn.GRU类初始化主要参数解释:
input_size: 输入张量x中特征维度的大小.
hidden_size: 隐层张量h中特征维度的大小.
num_layers: 隐含层的数量.
nonlinearity: 激活函数的选择, 默认是tanh.
bidirectional: 是否选择使用双向LSTM, 如果为True, 则使用; 默认不使用.
nn.GRU类实例化对象主要参数解释:
input: 输入张量x.
h0: 初始化的隐层张量h.
>>> import torch
>>> import torch.nn as nn
>>> rnn = nn.GRU(5, 6, 2)
>>> input = torch.randn(1, 3, 5)
>>> h0 = torch.randn(2, 3, 6)
>>> output, hn = rnn(input, h0)
>>> output
tensor([[[-0.2097, -2.2225, 0.6204, -0.1745, -0.1749, -0.0460],
[-0.3820, 0.0465, -0.4798, 0.6837, -0.7894, 0.5173],
[-0.0184, -0.2758, 1.2482, 0.5514, -0.9165, -0.6667]]],
grad_fn=<StackBackward>)
>>> hn
tensor([[[ 0.6578, -0.4226, -0.2129, -0.3785, 0.5070, 0.4338],
[-0.5072, 0.5948, 0.8083, 0.4618, 0.1629, -0.1591],
[ 0.2430, -0.4981, 0.3846, -0.4252, 0.7191, 0.5420]],
[[-0.2097, -2.2225, 0.6204, -0.1745, -0.1749, -0.0460],
[-0.3820, 0.0465, -0.4798, 0.6837, -0.7894, 0.5173],
[-0.0184, -0.2758, 1.2482, 0.5514, -0.9165, -0.6667]]],
grad_fn=<StackBackward>)
- GRU的优势:
GRU和LSTM作用相同, 在捕捉长序列语义关联时, 能有效抑制梯度消失或爆炸, 效果都优于传统RNN且计算复杂度相比LSTM要小. - GRU的缺点:
GRU仍然不能完全解决梯度消失问题, 同时其作用RNN的变体, 有着RNN结构本身的一大弊端, 即不可并行计算, 这在数据量和模型体量逐步增大的未来, 是RNN发展的关键瓶颈
RNN预测股价
任务:基于zgpa_train.csv数据,建立RNN模型,预测股价:
1.完成数据预处理,将序列数据转化为可用于RNN输入的数据
2.对新数据zgpa_test.csv进行预测,可视化结果
3.存储预测结果,并观察局部预测结果
备注:模型结构:单层RNN,输出有5个神经元;每次使用前8个数据预测第9个数据
import tensorflow as tf
tf.__version__
'1.13.1'
import pandas as pd
import numpy as np
data = pd.read_csv('zgpa_train.csv')
data.head()
date | open | high | low | close | volume | |
---|---|---|---|---|---|---|
0 | 2016-01-04 | 30.57 | 30.57 | 28.63 | 28.78 | 70997200 |
1 | 2016-01-05 | 28.41 | 29.54 | 28.23 | 29.23 | 87498504 |
2 | 2016-01-06 | 29.03 | 29.39 | 28.73 | 29.26 | 48012112 |
3 | 2016-01-07 | 28.73 | 29.25 | 27.73 | 28.50 | 23647604 |
4 | 2016-01-08 | 28.73 | 29.18 | 27.63 | 28.67 | 98239664 |
price = data.loc[:,'close']
price.head()
0 28.78
1 29.23
2 29.26
3 28.50
4 28.67
Name: close, dtype: float64
#归一化处理
price_norm = price/max(price)
print(price_norm)
0 0.383273
1 0.389266
2 0.389666
3 0.379545
4 0.381808
...
726 0.751099
727 0.750566
728 0.738447
729 0.733120
730 0.722466
Name: close, Length: 731, dtype: float64
%matplotlib inline
from matplotlib import pyplot as plt
fig1 = plt.figure(figsize=(8,5))
plt.plot(price)
plt.title('close price')
plt.xlabel('time')
plt.ylabel('price')
plt.show()
#define X and y
#define method to extract X and y
def extract_data(data,time_step):
X = []
y = []
#0,1,2,3...9:10个样本;time_step=8;0,1...7;1,2...8;2,3...9三组(两组样本)
for i in range(len(data)-time_step):
X.append([a for a in data[i:i+time_step]])
y.append(data[i+time_step])
X = np.array(X)
X = X.reshape(X.shape[0],X.shape[1],1)
return X, y
time_step = 8
#define X and y
X,y = extract_data(price_norm,time_step)
print(X[0,:,:])
print(y)
[[0.38327341]
[0.38926621]
[0.38966573]
[0.37954455]
[0.3818085 ]
[0.35637235]
0.7522972433080304, 0.7510986815821015, 0.7505659874816886, 0.7384471966972966, 0.7331202556931681, 0.7224663736849114]
print(X.shape,len(y))
(723, 8, 1) 723
#set up the model
from keras.models import Sequential
from keras.layers import Dense, SimpleRNN
model = Sequential()
#add RNN layer
model.add(SimpleRNN(units=5, input_shape=(time_step,1),activation='relu'))
#add output layer
model.add(Dense(units=1,activation='linear'))
#configure the model
model.compile(optimizer='adam',loss='mean_squared_error')
model.summary()
Using TensorFlow backend.
WARNING:tensorflow:From C:\Users\Eric\Anaconda3\lib\site-packages\tensorflow\python\framework\op_def_library.py:263: colocate_with (from tensorflow.python.framework.ops) is deprecated and will be removed in a future version.
Instructions for updating:
Colocations handled automatically by placer.
Model: "sequential_1"
_________________________________________________________________
Layer (type) Output Shape Param #
=================================================================
simple_rnn_1 (SimpleRNN) (None, 5) 35
_________________________________________________________________
dense_1 (Dense) (None, 1) 6
=================================================================
Total params: 41
Trainable params: 41
Non-trainable params: 0
_________________________________________________________________
#train the model
model.fit(X,y,batch_size=30,epochs=200)
WARNING:tensorflow:From C:\Users\Eric\Anaconda3\lib\site-packages\tensorflow\python\ops\math_ops.py:3066: to_int32 (from tensorflow.python.ops.math_ops) is deprecated and will be removed in a future version.
Instructions for updating:
Use tf.cast instead.
Epoch 1/200
723/723 [==============================] - 1s 958us/step - loss: 0.5179
Epoch 2/200
723/723 [==============================] - 0s 145us/step - loss: 0.4383
Epoch 3/200
723/723 [==============================] - 0s 146us/step - loss: 0.3743
Epoch 4/200
723/723 [==============================] - 0s 147us/step - loss: 0.3206
Epoch 5/200
723/723 [==============================] - 0s 163us/step - loss: 0.2947
Epoch 6/200
723/723 [==============================] - 0s 177us/step - loss: 0.2739
Epoch 7/200
723/723 [==============================] - 0s 163us/step - loss: 0.2544
Epoch 8/200
723/723 [==============================] - 0s 184us/step - loss: 0.2361
Epoch 9/200
723/723 [==============================] - 0s 186us/step - loss: 0.2190
Epoch 10/200
723/723 [==============================] - 0s 190us/step - loss: 0.2028
Epoch 11/200
723/723 [==============================] - 0s 184us/step - loss: 0.1877
Epoch 12/200
....
Epoch 193/200
723/723 [==============================] - 0s 218us/step - loss: 7.6791e-04
Epoch 194/200
723/723 [==============================] - 0s 211us/step - loss: 7.5809e-04
Epoch 195/200
723/723 [==============================] - 0s 186us/step - loss: 7.0882e-04
Epoch 196/200
723/723 [==============================] - 0s 195us/step - loss: 7.1198e-04
Epoch 197/200
723/723 [==============================] - 0s 201us/step - loss: 6.9356e-04
Epoch 198/200
723/723 [==============================] - 0s 199us/step - loss: 6.9895e-04
Epoch 199/200
723/723 [==============================] - 0s 197us/step - loss: 7.4995e-04
Epoch 200/200
723/723 [==============================] - 0s 188us/step - loss: 6.9783e-04
#make prediction based on the training data
y_train_predict = model.predict(X)*max(price)
y_train = [i*max(price) for i in y]
print(y_train_predict,y_train)
[[29.296108]
[26.819397]
…
[58.25873 ]
[58.098423]
[56.978115]] [26.72, 25.93, 25.800000000000004, 26.24, 25.26, 24.88, 24.95, 24.99, 23.96, 23.95, 24.029999999999998, 24.909999999999997, 24.26, 24.7, 24.37, 24.44, 24.39, 24.16, 24.849999999999998, 24.77, 24.64, 24.65, 25.7, 25.230000000000004, 25.14, 23.69, 23.829999999999995, 23.74, 23.99, 24.909999999999997, 25.11, 26.28, 25.78, 26.03, 26.110000000000003, 25.47, 25.77, 25.84, 26.09, 26.86, 26.73, 26.85, 27.559999999999995, 26.83, 26.989999999999995, 26.53, 26.79, 26.19, 25.98, 26.72, 26.54, 26.65, 26.73, 26.47, 26.1, 25.89, 26.24, 26.28, 26.64, 26.75, 26.72, 26.52, 26.66, 26.94, 26.95, 27.15, 27.05, 27.119999999999997, 26.989999999999995, 26.92, 26.77, 26.64, 26.66, 26.680000000000003, 26.13, 25.89, 25.92, 25.98, 26.44, 26.45, 26.34, 26.24, 26.54, 26.56, 26.55, 26.56, 26.370000000000005, 26.34, 26.359999999999996, 26.33, 26.64, 27.45, 27.02, 26.94, 27.07, 26.84, 26.869999999999997, 26.88, 26.53, 26.71, 26.71, 26.7, 26.71, 26.85, 26.79, 26.81, 26.73, 26.28, 26.41, 26.58, 26.89, 26.77, 26.91, 27.17, 27.15, 27.07, 27.03, 26.86, 26.869999999999997, 27.49, 27.54, 27.57, 27.67, 27.77, 27.46, 27.39, 27.63, 27.38, 27.38, 27.6, 27.87, 27.53, 27.45, 27.36, 27.36, 27.26, 27.23, 27.26, 27.35, 27.62, 27.62, 27.87, 28.56, 29.64, 29.0, 29.05, 29.51, 29.6, 29.54, 29.92, 29.41, 29.17, 29.17, 29.34, 29.29, 29.3, 29.45, 29.94, 30.04, 30.42, 30.49, 30.660000000000004, 30.74, 30.399999999999995, 30.0, 29.580000000000002, 30.25, 30.13, 29.95, 30.29, 30.17, 29.66, 29.64, 29.42, 29.47, 29.44, 29.88, 29.66, 29.580000000000002, 29.4, 29.39, 29.22, 29.48, 29.4, 29.42, 29.5, 29.769999999999996, 29.829999999999995, 29.7, 29.47, 29.81, 29.89, 30.339999999999996, 29.89, 30.25, 30.21, 30.27, 30.279999999999998, 29.99, 30.22, 30.27, 30.23, 30.24, 30.3, 30.279999999999998, 30.29, 31.18, 31.4, 31.26, 31.7, 32.06, 31.89, 32.05, 31.61, 31.7, 31.55, 31.540000000000003, 31.45, 31.74, 31.78, 32.14, 32.18, 31.93, 32.31, 30.88, 30.76, 30.68, 29.94, 30.02, 30.05, 30.01, 30.399999999999995, 30.54, 30.57, 30.410000000000004, 30.71, 30.96, 30.93, 31.01, 30.720000000000002, 30.7, 30.51, 30.51, 30.49, 30.84, 31.47, 31.26, 31.49, 31.33, 31.540000000000003, 31.480000000000004, 31.46, 31.71, 31.789999999999996, 30.9, 31.219999999999995, 31.34, 31.3, 31.39, 31.480000000000004, 31.59, 31.38, 31.6, 31.58, 31.540000000000003, 32.1, 32.05, 32.1, 31.800000000000004, 31.84, 31.6, 31.51, 31.44, 31.14, 31.13, 31.11, 31.24, 31.099999999999998, 31.06, 30.88, 31.25, 31.219999999999995, 31.24, 31.35, 31.03, 31.11, 31.55, 31.28, 31.85, 31.83, 32.02, 32.01, 32.06, 32.03, 32.29, 32.38, 32.24, 31.88, 31.729999999999997, 31.47, 31.290000000000003, 31.18, 31.170000000000005, 31.3, 30.98, 30.79, 31.08, 31.37, 31.45, 31.76, 32.79, 33.1, 33.24, 33.19, 33.28, 33.1, 32.54, 33.07, 33.27, 35.12, 35.05, 36.23, 36.18, 36.37, 35.7, 35.9, 36.29, 38.12, 38.45, 38.31, 40.77, 40.46, 40.35, 41.25, 40.24, 39.95, 41.31000000000001, 41.26, 42.9, 43.42, 44.21000000000001, 43.72, 42.49, 41.46, 41.97999999999999, 43.84, 43.379999999999995, 44.71000000000001, 44.42, 45.0, 45.0, 45.2, 43.89, 45.24, 44.89, 44.22, 43.2, 45.7, 46.67, 46.29, 46.41, 47.06, 46.44, 47.46, 48.6, 49.73, 49.3, 49.67, 49.84000000000001, 47.79, 48.44, 48.14, 47.72, 48.19, 47.72, 47.85, 50.68, 49.86999999999999, 48.75000000000001, 47.77, 48.15, 48.28, 47.89, 46.56, 45.41, 46.32, 46.73, 47.03999999999999, 46.93, 49.0, 48.86, 51.03000000000001, 51.23, 50.95, 51.66, 52.029999999999994, 52.89, 52.24, 51.86, 52.0, 51.48, 52.62, 51.73, 51.24, 51.35, 50.31999999999999, 51.14, 51.4, 50.35, 49.71, 49.63, 49.79, 49.41, 50.15, 50.37, 50.49, 50.580000000000005, 49.93, 50.07, 50.49, 51.09, 51.25, 51.64, 53.44, 53.40999999999999, 53.5, 54.27, 55.31, 55.92, 55.35, 56.21000000000001, 56.53, 56.41, 57.72, 60.26, 60.39, 60.69, 60.45, 60.47, 60.78, 60.34, 61.699999999999996, 60.94, 62.66, 66.22, 66.48, 66.75, 65.53, 69.34, 71.6, 72.04, 74.77, 73.78, 70.65, 70.95, 69.88, 69.06, 68.75, 66.25, 64.43, 65.72, 67.53, 65.73, 64.97, 67.82, 69.58, 67.54, 68.45, 67.34, 66.71, 67.0, 69.91, 70.27, 71.24, 70.35, 70.49, 70.27, 66.68, 66.92, 66.31, 68.92, 67.3, 67.51, 67.22, 66.34, 68.2, 68.05, 68.98, 70.74, 73.98, 74.14, 73.67, 73.94, 73.92, 75.07, 75.09, 72.77, 71.62, 72.43, 69.69, 69.71, 71.41, 71.81, 72.34, 72.04, 70.1, 67.71, 65.32, 60.76, 61.29, 63.35, 65.22, 66.1, 66.97, 67.13, 66.14, 64.09, 65.26, 64.01, 64.21, 65.65, 65.11, 66.87, 67.22, 67.45, 65.75, 65.15, 66.82, 66.84, 70.14,