الشبكة العصبية المتكررة Recurrent Neural Network باسخدام Tensorflow – (الجزء الثاني)

170

RNN في سلسلة زمنية

في هذا المقال، ستستخدم RNN مع بيانات متسلسلة زمنياً. تتغير قيم السلسلة الزمنية على مدار الوقت بالطبع، مما يعني أن القيم السابقة تحتوي على معلومات ذات صلة يمكن للشبكة أن تتعلم منها. إن الفكرة وراء التنبؤ بسلسلة زمنية هي تقدير القيمة المستقبلية، مثل، سعر السهم، درجة الحرارة، الناتج المحلي الإجمالي وما إلى ذلك.

يمكن أن يكون إعداد البيانات والشبكة نفسها صعباً بعض الشيء:

أولاً وقبل كل شيء، تذكر أن الهدف هو التنبؤ بالقيمة التالية من السلسلة، بمعنى أنك ستستخدم المعلومات السابقة لتقدير القيمة التي تليهاt+1.

ثانيًا، يتم تعيين عدد المدخلات ل 1، أي واحد في كل مرة. وأخيرا، فإن قيمة time steps (الخطوات الزمنية) تساوي القيمة العددية المنتجة في هذه السلسلة. على سبيل المثال، إذا قمت بتعيين الخطوة الزمنية إلى 10، فستحصل على تسلسل من المدخلات عشر مرات متتالية.

انظر إلى الرسم البياني أدناه، لقد قمنا بعرض بيانات لسلسلة الزمنية على اليسار وسلسلة اخري تخيلية على اليمين “للتدريب”. ثم إنشاء دالة لإرجاع بيانات عشوائية لكل يوم من يناير 2001 إلى ديسمبر 2016.

# To plot pretty figures
%matplotlib inline
import matplotlib
import matplotlib.pyplot as plt
import pandas as pd
def create_ts(start = '2001', n = 201, freq = 'M'):
    rng = pd.date_range(start=start, periods=n, freq=freq)
    ts = pd.Series(np.random.uniform(-18, 18, size=len(rng)), rng).cumsum()
    return ts
ts= create_ts(start = '2001', n = 192, freq = 'M')
ts.tail(5)

النتيجة

2016-08-31    -93.459631
2016-09-30    -95.264791
2016-10-31    -95.551935
2016-11-30   -105.879611
2016-12-31   -123.729319
Freq: M, dtype: float64

 

ts = create_ts(start = '2001', n = 222)

# Left
plt.figure(figsize=(11,4))
plt.subplot(121)
plt.plot(ts.index, ts)
plt.plot(ts.index[90:100], ts[90:100], "b-", linewidth=3, label="A training instance")
plt.title("A time series (generated)", fontsize=14)

# Right
plt.subplot(122)
plt.title("A training instance", fontsize=14)
plt.plot(ts.index[90:100], ts[90:100], "b-", markersize=8, label="instance")
plt.plot(ts.index[91:101], ts[91:101], "bo", markersize=10, label="target", markerfacecolor='red')
plt.legend(loc="upper left")
plt.xlabel("Time")

plt.show()

النتيجة

الجزء الأيمن من الرسم البياني يعرض البيانات كلها. يبدأ من عام 2001 وينتهي عند عام 2019 لا مغزى من تواجد جميع البيانات في الشبكة، بدلاً من ذلك، تحتاج إلى إنشاء مجموعة من البيانات بطول يساوي قيمة الخطوات الزمنية. هذه الدفعات ستتكون من المتغير X والمتغير Y. وللعلم Y هو نفسه X، ولكن تم زحزحته بمقدار نقطة واحدة (أي أنك تريد القيمة التالية t+1).

كلا المتجهين لها نفس الطول. يمثل X القيم العشرة للإدخال، في حين أن النقاط الحمراء العشرY. لاحظ أن التسمية تبدأ فترة زمنية متأخرة وتنتهي فترة زمنية متقدمة.

 

بناء RNN للتنبؤ بالسلسلة الزمنية في TensorFlow

الآن، حان الوقت لبناء أول RNN للتنبؤ بالسلسلة أعلاه. تحتاج إلى تحديد بعض المتغيرات (المتغيرات التي سيتم تمريرها للنموذج، عدد الخلايا العصبية، إلخ):

  • عدد المدخلات: 1
  • الخطوة الزمنية: 10
  • عدد الخلايا العصبية: 120
  • عدد المخرجات: 1

ستتعلم شبكتك من سلسلة من بيانات 10 أيام وتحتوي على 120 خلية عصبية متكررة. نقوم بتمرير مدخل واحد للنموذج، أي يوم واحد. يمكنك تغيير القيم لمعرفة ما إذا كان النموذج سيعمل بشكل أفضل أم لا.

قبل إنشاء النموذج، تحتاج إلى تقسيم مجموعة البيانات إلى مجموعة للتدريب ومجموعة للاختبار. تحتوي مجموعة البيانات الكاملة على 222 نقطة. ستستخدم أول نقطة 201 لتدريب النموذج وآخر 21 نقطة لاختبار نموذج.

بعد تحديد مجموعة التدريب ومجموعة الاختبار، تحتاج إلى إنشاء هيكل بيانات يحمل كل هذه الدفعات. يوجد لديك قيم X وقيم Y. تذكر أن قيم X توجد بها قيمة مزحزحه للوراء خطوة. لذلك، تستخدم أول 200 نقطة وtimestep تساوي 10. يجب أن تحتوي الدفعات X على 20 دفعه بحجم 10*1. تحتوي الدفعات y على نفس الشكل ولكن مع قيمة واحدة مزحزحه للأمام.

الخطوة الاولي: مجموعة التدريب ومجموعة الاختبار

بادئ ذي بدء، تقوم بتحويل سلسلة البيانات هذه إلى array (مصفوفة) numpy ثم تحدد عدد المرات التي ستقوم فيها الشبكة بالتعلم (أي n_windows (عدد النوافذ))، وعدد المدخلات والمخرجات وحجم مجموعة التدريب.

series = np.array(ts)
n_windows = 20   
n_input =  1
n_output = 1
size_train = 201

بعد ذلك، يمكنك ببساطة تقسيم array إلى مجموعتي بيانات.

## Split data
train = series[:size_train]
test = series[size_train:]
print(train.shape, test.shape)
(201,) (21,)

الخطوة الثانية: إنشاء الدالة لتحضير الدفعات X_batches وy_batches

لتسهيل الأمر، يمكنك إنشاء دالة تقوم بإرجاع 2 arrays المختلفين، واحدة لـ X_batches وواحدة لـ y_batches.

لاحظ أنه، يتم زحزحه دفعات X بقيمة واحدة (نأخذ قيمة t-1). يجب أن يكون ناتج الدالة ثلاثة أبعاد. البعد الأولى يساوي عدد الدُفعات، والثاني حجم النوافذ وآخر رقم الإدخال.

الجزء الاصعب هو تحديد نقاط البيانات بشكل صحيح. بالنسبة لنقاط بيانات X، تختار النقاط من t = 1 إلى t = 200، بينما بالنسبة لنقاط بيانات Y، تختار النقاط من t = 2 إلى 201. وبمجرد أن يكون لديك نقاط البيانات الصحيحة، يكون من السهل إعادة تشكيل السلسلة.

تحتاج الى تقسيم مجموعة البيانات إلى عشر دفعات متساوية (أي 20 لكل دفعة). يمكنك استخدام وظيفة reshape (إعادة تشكيل) وتمرير -1 يعني أن الحجم في ذلك البعد “البعد X” مستنتج مسبقاً. أي كلا من حجم الدفعة والسلسلة متماثلين. القيمة 20 هي حجم الدفعة و1 هو عدد المدخلات.

تحتاج إلى القيام بنفس الخطوة ل y ايضا.

لاحظ أنك تحتاج إلى تحريك البيانات نحو الوقت الذي تريد التنبؤ به. على سبيل المثال، إذا كنت ترغب بالتنبؤ باليوم التالي فقط، فحينئذٍ يمكنك التحرك بمقدار خطوة واحدة في السلسلة. إذا كنت تريد أن تتنبأ باليومين القادمين، فقم بالتحرك بمقدار خطوتين.

x_data = train[:size_train-1]: Select all the training instance minus one day
X_batches = x_data.reshape(-1, windows, input): create the right shape for the batch e.g (10, 20, 1)
def create_batches(df, windows, input, output):
    ## Create X         
        x_data = train[:size_train-1] # Select the data
        X_batches = x_data.reshape(-1, windows, input)  # Reshape the data 
    ## Create y
        y_data = train[n_output:size_train]
        y_batches = y_data.reshape(-1, windows, output)
        return X_batches, y_batches

ولان يمكنك لإنشاء الدفعات.

X_batches, y_batches = create_batches(df = train,
                                      windows = n_windows,
                                      input = n_input,
                                      output = n_output)

يمكنك طباعة الشكل الحالي للتأكد من صحة الأبعاد.

print(X_batches.shape, y_batches.shape)
(10, 20, 1) (10, 20, 1)

تحتاج إلى إنشاء مجموعة اختبار من دفعة واحدة فقط من البيانات.

لاحظ أنك تتنبأ ب أيامًا بعد أيام، فهذا يعني أن القيمة المتوقعة الثانية ستعتمد على القيمة الحقيقية “التي تم التنبؤ بها سابقا” من مجموعة بيانات الاختبار.

إذا كنت ترغب في توقع t + 2 (أي بعد يومين)، فستحتاج إلى استخدام القيمة المتوقعة t + 1؛ إذا كنت تتوقع T + 3 (بعد ثلاثة أيام)، فستحتاج إلى استخدام القيم المتوقعة t + 1 وt + 2. من المنطقي أن يكون من الصعب التنبؤ بدقة.

X_test, y_test = create_batches(df = test, windows = 20,input = 1, output = 1)
print(X_test.shape, y_test.shape)
(10, 20, 1) (10, 20, 1)

الخطوة الثالثة: بناء النموذج

لإنشاء النموذج، يجب تحديد ثلاثة أجزاء:

  • المتغيرات
  • إنشاء RNN
  • التكلفة والتحسين

المتغيرات

تحتاج إلى تحديد المتغيرات X وy بالشكل المناسب. فيجب ان يصبح كلا من X_batches وy_batches يملكوا نفس البعد.

على سبيل المثال، فإن لكل دفعة في X_batches هو placeholder (عنصر نائب) له ثلاثة أبعاد:

Note: حجم الدفعة.

n_windows: حجم النوافذ. (أي عدد المرات التي ينظر فيها النموذج للخلف للتعلٌم).

n_input: عدد المدخلات.

tf.placeholder(tf.float32, [None, n_windows, n_input]) 	
## 1. Construct the tensors
X = tf.placeholder(tf.float32, [None, n_windows, n_input])   
y = tf.placeholder(tf.float32, [None, n_windows, n_output])

إنشاء RNN

تحتاج الان إلى تحديد معمارية الشبكة. يمكنك استخدام BasicRNNCell وdynamic_rnn من TensorFlow.

## 2. create the model
basic_cell = tf.contrib.rnn.BasicRNNCell(num_units=r_neuron, activation=tf.nn.relu)   
rnn_output, states = tf.nn.dynamic_rnn(basic_cell, X, dtype=tf.float32) 

الجزء التالي هو الأكثر تعقيدًا ولكنه مفيد للغاية. تحتاج إلى إعادة تشكيل هيئة المخرجات لإدخالها الى الطبقة الكثيفة، ثم تحويلها مرة أخرى ليكون له نفس ابعاد المدخلات.

stacked_rnn_output = tf.reshape(rnn_output, [-1, r_neuron])          
stacked_outputs = tf.layers.dense(stacked_rnn_output, n_output)       
outputs = tf.reshape(stacked_outputs, [-1, n_windows, n_output])  

التكلفة والتحسين

تتمثل مشكلة التحسين للمتغير المستمر هذا في تقليل mean square error (متوسط خطأ المربع). للقيام بذلك في TF، يمكنك استخدام:

  • (reduce_sum(tf.square(outputs - y

يمكنك أيضا ان تستخدم Adam optimizer (محسن آدم) لتقليل التكلفة:

  • (train.AdamOptimizer(learning_rate=learning_rate
  • (minimize(loss

هذا كل شيء، نموذجك الان جاهزًا للتدريب.

tf.reset_default_graph()
r_neuron = 120    

## 1. Construct the tensors
X = tf.placeholder(tf.float32, [None, n_windows, n_input])   
y = tf.placeholder(tf.float32, [None, n_windows, n_output])

## 2. create the model
basic_cell = tf.contrib.rnn.BasicRNNCell(num_units=r_neuron, activation=tf.nn.relu)   
rnn_output, states = tf.nn.dynamic_rnn(basic_cell, X, dtype=tf.float32)              

stacked_rnn_output = tf.reshape(rnn_output, [-1, r_neuron])          
stacked_outputs = tf.layers.dense(stacked_rnn_output, n_output)       
outputs = tf.reshape(stacked_outputs, [-1, n_windows, n_output])   

## 3. Loss + optimization
learning_rate = 0.001  
 
loss = tf.reduce_sum(tf.square(outputs - y))    
optimizer = tf.train.AdamOptimizer(learning_rate=learning_rate)         
training_op = optimizer.minimize(loss)                                          

init = tf.global_variables_initializer() 

سوف تقوم بتدريب النموذج باستخدام 1500 نقطة وطباعة التكلفة كل 150 لفه. بمجرد تدريب النموذج، تقوم بتقييم النموذج بناء على مجموعة الاختبار ثم عرض التنبؤات.

iteration = 1500 

with tf.Session() as sess:
    init.run()
    for iters in range(iteration):
        sess.run(training_op, feed_dict={X: X_batches, y: y_batches})
        if iters % 150 == 0:
            mse = loss.eval(feed_dict={X: X_batches, y: y_batches})
            print(iters, "\tMSE:", mse)
    
    y_pred = sess.run(outputs, feed_dict={X: X_test})
0 	MSE: 502893.34
150 	MSE: 13839.129
300 	MSE: 3964.835
450 	MSE: 2619.885
600 	MSE: 2418.772
750 	MSE: 2110.5923
900 	MSE: 1887.9644
1050 	MSE: 1747.1377
1200 	MSE: 1556.3398
1350 	MSE: 1384.6113

في النهاية، يمكنك رسم كلا من القيمة الفعلية بالقيمة المتوقعة في مخطط بياني. إذا عمل نموذجك كما هو مطلوب، فيجب ان تكون القيم المتوقعة أقرب ما يكون للقيم الفعلية.

كما ترون، يمكن القيام ببعض التحسينات في النموذج. الأمر متروك لك لتغيير hyperparameters مثل حجم النوافذ، وحجم دفعة البيانات من عدد الخلايا العصبية المتكررة.

plt.title("Forecast vs Actual", fontsize=14)
plt.plot(pd.Series(np.ravel(y_test)), "bo", markersize=8, label="Actual", color='green')
plt.plot(pd.Series(np.ravel(y_pred)), "r.", markersize=8, label="Forecast", color='red')
plt.legend(loc="lower left")
plt.xlabel("Time")

plt.show()

للتلخيص

الشبكة العصبية المتكررة هي معمارية قوية للتعامل مع السلسلة الزمنية أو تحليل النصوص. ناتج السابق يتم الاحتفاظ به داخل ذاكرة الشبكة مع مرور الوقت لاستعماله مرة اخري علي هيئه مدخلات مرة اخري.

في TensorFlow، يمكنك البرنامج التالي لتدريب شبكة عصبية متكررة للسلسلة الزمنية:

معلمات النموذج:

n_windows = 20   
n_input =  1
n_output = 1
size_train = 201

انشاء النموذج

X = tf.placeholder(tf.float32, [None, n_windows, n_input])   
y = tf.placeholder(tf.float32, [None, n_windows, n_output])

basic_cell = tf.contrib.rnn.BasicRNNCell(num_units=r_neuron, activation=tf.nn.relu)   
rnn_output, states = tf.nn.dynamic_rnn(basic_cell, X, dtype=tf.float32)              

stacked_rnn_output = tf.reshape(rnn_output, [-1, r_neuron])          
stacked_outputs = tf.layers.dense(stacked_rnn_output, n_output)       
outputs = tf.reshape(stacked_outputs, [-1, n_windows, n_output])

انشاء المحسن

learning_rate = 0.001  
 
loss = tf.reduce_sum(tf.square(outputs - y))    
optimizer = tf.train.AdamOptimizer(learning_rate=learning_rate)         
training_op = optimizer.minimize(loss)         

تدريب النموذج

init = tf.global_variables_initializer() 
iteration = 1500 

with tf.Session() as sess:
    init.run()
    for iters in range(iteration):
        sess.run(training_op, feed_dict={X: X_batches, y: y_batches})
        if iters % 150 == 0:
            mse = loss.eval(feed_dict={X: X_batches, y: y_batches})
            print(iters, "\tMSE:", mse)
    
    y_pred = sess.run(outputs, feed_dict={X: X_test})

 

المصدر:(RNN(Recurrent Neural Network) Tutorial: TensorFlow Example)

تعليقات