الأحد، 7 يوليو 2019

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

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

سوف تتعلم في هذا الدرس بناء البنية العامة لخوارزمية التعلم، بما في ذلك:
  • تهيئة المعاملات "Initializing parameters"
  • حساب دالة التكلفة وتدرجها "Calculating the cost function and its gradient"
  • استخدام الانحدار التدريجي للتحسين "Using an optimization algorithm (gradient descent)" 
جمع ما ذكر اعلاه في نموذج واحد بالترتيب الصحيح.

مصطلحات:
  • Logistic Regression --> انحدار لوجستي
  • Linear Regression --> انحدار خطي
  • Neural Networks --> شبكات عصبية
  • Packages --> الحزم البرمجية
  • Array or Matrix --> مصفوفة
  • Vector --> متجهة
###############

لتنزيل البايثون 3 

طريقة تحميل حزم البايثون على الجهاز

المصدر الاساسي للدرس

المعادلات الرياضية موجودة في الدرس الكامل للمهتمين

##############

في البداية، نستورد كافة الحزم البرمجية التي سوف نحتاجها خلال هذا الدرس:
  • numpy هي الحزمة الأساسية للحوسبة العلمية للبايثون.
  • h5py حزمة شائعة للتفاعل مع مجموعة بيانات مخزنة على ملف (H5).
  • matplotlib هي مكتبة مشهورة لرسم الرسوم البيانية.
  • PIL and scipy تستخدم هنا لاختبار النموذج الخاص بك مع الصورة الخاصة بك في نهاية الدرس.

import numpy as np
import matplotlib.pyplot as plt
import h5py
import scipy
from PIL import Image
from scipy import ndimage
from lr_utils import load_dataset
%matplotlib inline

نظرة عامة

يتم منحك مجموعة بيانات في data.h5 تحتوي على:
  • مجموعة من الصور المعلمة بنوعها سواء قطة ام لا بغرض التدريب "a training set of m_train images labeled as cat (y=1) or non-cat (y=0)"
  • مجموعة من الصور المعلمة بنوعها سواء قطة ام لا بغرض الاختبار "a test set of m_test images labeled as cat or non-cat"
سوف نقوم ببناء شبكة عصبية يمكنها تصنيف الصور بشكل صحيح لقط أو غير قط. يمكننا تحميل البيانات بواسطة الكود الاتي للتعرف على  البيانات بشكل أكبر.

# Loading the data (cat/non-cat)
train_set_x_orig, train_set_y, test_set_x_orig, test_set_y, classes = load_dataset()

لاحظ إضافة  "orig"  الى حزم الصور (train_set_x_orig and test_set_x_orig). الهدف من ذلك للتنبية انها البيانات الاوليه قبل المعالجة وبعد المعالجة سيتم ازالة "orig"  لتصبح (train_set_x and test_set_x) 
  
كل سطر من قاعدتي البيانات (train_set_x_orig و test_set_x_orig) هو مصفوفة يمثل صورة "an array representing an image".
يمكنك اظهار أحد الصور باستخدام هذا الكود التالي. تستطيع تغيير قيمة الفهرس لرؤية الصور الأخرى. 

"Feel free also to change the index value and re-run to see other images" 

# Example of a picture
# Feel free also to change the index value and re-run to see other images
index = 25
plt.imshow(train_set_x_orig[index])
print ("y = " + str(train_set_y[:, index]) + ", it's a '" + classes[np.squeeze(train_set_y[:, index])].decode("utf-8") +  "' picture.")

Expected Output
y = [1], it's a 'cat' picture.

 




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

الان لنجد القيم التالية:
  • عدد أمثلة التدريب "m_train (number of training examples)"
  • عدد امثلة الاختبار "m_test (number of test examples)"
  • عدد البكسلات لصورة تدريب واحدة "num_px (= height = width of a training image)". الصورة مربعه (الطول = العرض).

# np.shape يعطيك ابعاد المصفوفة
# بايثون يبدأ يعد من الصفر
m_train = train_set_y.shape[1] #عدد أمثلة التدريب
m_test = test_set_y.shape[1] #عدد امثلة الاختبار
num_px = train_set_x_orig.shape[1] # عدد البكسلات 


#The str() function converts the specified value into a string.

print ("Number of training examples: m_train = " + str(m_train))
print ("Number of testing examples: m_test = " + str(m_test))
print ("Height/Width of each image: num_px = " + str(num_px))
print ("Each image is of size: (" + str(num_px) + ", " + str(num_px) + ", 3)")
print ("train_set_x shape: " + str(train_set_x_orig.shape))
print ("train_set_y shape: " + str(train_set_y.shape))
print ("test_set_x shape: " + str(test_set_x_orig.shape))
print ("test_set_y shape: " + str(test_set_y.shape))

Expected Output
Number of training examples: m_train = 209
Number of testing examples: m_test = 50
Height/Width of each image: num_px = 64
Each image is of size: (64, 64, 3)
train_set_x shape: (209, 64, 64, 3)
train_set_y shape: (1, 209)
test_set_x shape: (50, 64, 64, 3)
test_set_y shape: (1, 50)

للتسهيل، عليك اعادة تشكيل الصور من (num_px, num_px, 3) الى (num_px ∗ num_px ∗ 3, 1). بعد ذلك كل سطر في مجموعة بيانات التدريب والاختبار سيمثل بيانات صورة واحدة.

# اعادة تشكيل البيانات

train_set_x_flatten = train_set_x_orig.reshape(train_set_x_orig.shape[0], -1).T #بيانات التدريب
test_set_x_flatten = test_set_x_orig.reshape(test_set_x_orig.shape[0], -1).T #بيانات الاختبار

print ("train_set_x_flatten shape: " + str(train_set_x_flatten.shape))
print ("train_set_y shape: " + str(train_set_y.shape))
print ("test_set_x_flatten shape: " + str(test_set_x_flatten.shape))
print ("test_set_y shape: " + str(test_set_y.shape))
print ("sanity check after reshaping: " + str(train_set_x_flatten[0:5,0]))

Expected Output
train_set_x_flatten shape: (12288, 209)
train_set_y shape: (1, 209)
test_set_x_flatten shape: (12288, 50)
test_set_y shape: (1, 50)
sanity check after reshaping: [17 31 56 22 33]

لتمثيل الصور الملونة، لا بد ان تحدد القنوات الحمراء والخضراء والزرقاء لكل بكسل. فعليا، كل بكسل سيمثل من ثلاثة ارقام كل رقم من صفر الى 255. هذا يعرف ب RGB system.

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

train_set_x = train_set_x_flatten/255.
test_set_x = test_set_x_flatten/255.

البنية العامة لخوارزمية التعلم 

حان الوقت لتصميم خوارزمية بسيطة لتمييز صور القطط عن الصور غير القطط. سوف نبني شبكة عصبية باستخدام الانحدار اللوجستي.

الشكل التالي يفسر لماذا الانحدار اللوجستي هو في الواقع شبكة عصبية بسيطة جدا!

بناء أجزاء من الخوارزمية 


الخطوات الرئيسية لبناء شبكة العصبية هي:

  1. تحديد النموذج الحسابي كعدد الميزات (Define the model structure (such as number of input features.
  2. تهيئة المعاملات Initialize the model's parameters.
  3. حلقة التكرارloop وتشمل:
  • حساب الفقد الحالي بالانتشار الامامي (Calculate current loss (forward propagation.
  • حساب التدرج الحالي بالانتشار الخلفي (Calculate current gradient (backward propagation.
  • تحديث المعاملات ب (Update parameters (gradient descent.

 في كثير من الأحيان، يمكنك بناء كل واحدة بشكل منفصل ودمجها في وظيفة واحدة.

#نبدأ بتعريف الدالة اللوجستية
def sigmoid(z):
    s = 1 / (1 + np.exp(-z))

    return s

print ("sigmoid([0, 2]) = " + str(sigmoid(np.array([0,2]))))

Expected Output
sigmoid([0, 2]) = [ 0.5         0.88079708]

تهيئة المعاملات


# نبدأ المعاملات كلها من صفر

def initialize_with_zeros(dim):
    w = np.zeros(shape=(dim, 1))
    b = 0


    assert(w.shape == (dim, 1))
    assert(isinstance(b, float) or isinstance(b, int))
    
    return w, b

dim = 2 #للتجربة
w, b = initialize_with_zeros(dim)
print ("w = " + str(w))
print ("b = " + str(b))

Expected Output
w = [[ 0.]
 [ 0.]]
b = 0

الانتشار الامامي والخلفي 


الآن بعد أن تم تهيئة المعلمات الخاصة بك، يمكنك القيام بخطوات الانتشار "إلى الأمام" و "الخلف" لضبط المعلمات. الان سننفذ الدالة `() propagate` التي تحسب دالة التكلفة وتدرجها.

def propagate(w, b, X, Y):
    """
    Implement the cost function and its gradient for the propagation explained above #حساب التكلفة والتدرج

    Arguments: # المدخلات
    w -- weights, a numpy array of size (num_px * num_px * 3, 1) #  اوزان اكس او الصور
    b -- bias, a scalar #اوزان تعديل الانحياز
    X -- data of size (num_px * num_px * 3, number of examples) # حجم البيانات كامل
    Y -- true "label" vector (containing 0 if non-cat, 1 if cat) of size (1, number of examples) # رممز التعريف للعينات
 
    Return: # العائد
    cost -- negative log-likelihood cost for logistic regression # قيمة التكلفة
    dw -- gradient of the loss with respect to w, thus same shape as w
    db -- gradient of the loss with respect to b, thus same shape as b
    
    """
    
    m = X.shape[1]
    
    # FORWARD PROPAGATION (FROM X TO COST) الانتشار الامامي من اكس الى التكلفه
    A = sigmoid(np.dot(w.T, X) + b)  # compute activation 
    cost = (- 1 / m) * np.sum(Y * np.log(A) + (1 - Y) * (np.log(1 - A)))  # compute cost حساب التكلفة
    
    # BACKWARD PROPAGATION (TO FIND GRAD) الانتشار الخلفي لضبط المعلمات
    dw = (1 / m) * np.dot(X, (A - Y).T)
    db = (1 / m) * np.sum(A - Y)

    assert(dw.shape == w.shape)
    assert(db.dtype == float)
    cost = np.squeeze(cost)
    assert(cost.shape == ())
    
    grads = {"dw": dw,
             "db": db}
    
    return grads, cost

w, b, X, Y = np.array([[1.],[2.]]), 2., np.array([[1.,2.,-1.],[3.,4.,-3.2]]), np.array([[1,0,1]]) # للتجربة
grads, cost = propagate(w, b, X, Y)
print ("dw = " + str(grads["dw"]))
print ("db = " + str(grads["db"]))
print ("cost = " + str(cost))

Expected Output
dw = [[ 0.99845601]
 [ 2.39507239]]
db = 0.00145557813678
cost = 5.80154531939


تحقيق الحالة المثلى او تحسين البحث 

  • لقد هيئت معاملاتك You have initialized your parameters.
  • بإمكانك حساب دالة التكلفة وتدرجها You are also able to compute a cost function and its gradient.
  • الان، ستستخدم النزول المتدرج لتحديث وتحسين معاملاتك Now, you want to update the parameters using gradient descent. 

def optimize(w, b, X, Y, num_iterations, learning_rate, print_cost = False): # هذة الدالة تعديل وامتداد للدالة السابقة
    """
    This function optimizes w and b by running a gradient descent algorithm
    
    Arguments: # المدخلات
    w -- weights, a numpy array of size (num_px * num_px * 3, 1) #نفس الدالة السابقة
    b -- bias, a scalar  #نفس الدالة السابقة
    X -- data of shape (num_px * num_px * 3, number of examples)  #نفس الدالة السابقة
    Y -- true "label" vector (containing 0 if non-cat, 1 if cat), of shape (1, number of examples)  #نفس الدالة السابقة
    num_iterations -- number of iterations of the optimization loop # عدد التكرارات
    learning_rate -- learning rate of the gradient descent update rule #معدل التعلم
    print_cost -- True to print the loss every 100 steps # خيار طباعة التكلفة
    
    Returns: # العائد
    params -- dictionary containing the weights w and bias b# قاموس يحتوي على قيم اوزان اكس وتعديل الانحياز
    grads -- dictionary containing the gradients of the weights and bias with respect to the cost function #قاموس يحتوي على قيم النزول للأوزان
    costs -- list of all the costs computed during the optimization, this will be used to plot the learning curve. # يحتوي على كل قيم التكلفة للأستخدام فيما بعد
    
    """
    
    costs = []
    
    for i in range(num_iterations): # حلقة التكرار
        
        
        # Cost and gradient calculation
        grads, cost = propagate(w, b, X, Y) #نستخدم الدالة السابقة
        
        # Retrieve derivatives from grads 
        dw = grads["dw"]
        db = grads["db"]
        
        # update rule 
        w = w - learning_rate * dw  #  كل تكرار او خطوة يغيرلك القيم بناء على معدل التعلم الي تحددة
        b = b - learning_rate * db

        # Record the costs
        if i % 100 == 0: #يحسب التكلفة كل 100 خطوة
            costs.append(cost)
        
        # Print the cost every 100 training examples
        if print_cost and i % 100 == 0:
            print ("Cost after iteration %i: %f" % (i, cost))
    
    params = {"w": w,
              "b": b}
    
    grads = {"dw": dw,
             "db": db}
    
    return params, grads, costs

#-------
params, grads, costs = optimize(w, b, X, Y, num_iterations= 100, 
learning_rate = 0.009, print_cost = False) #تجربة

print ("w = " + str(params["w"]))
print ("b = " + str(params["b"]))
print ("dw = " + str(grads["dw"]))
print ("db = " + str(grads["db"]))

Expected Output
w = [[ 0.19033591]
 [ 0.12259159]]
b = 1.92535983008
dw = [[ 0.67752042]
 [ 1.41625495]]
db = 0.219194504541


الدالة (او مجموعة الدوال السابقة) بتعطيكقيم w و b بناء على عدد التكرارات. طبعا الان تقدر تستخدم هذا النموذج للتنبؤ بنوع الصورة إذا كانت قطه ام لا. الي بتسوية أنك تأخذ الصور الي تعتبر X وتحولها ال Y. إذا كانت قيمة التنشيط أكثر من 0.5 النموذج سوف يعتبر Y=1 او صورة قطة، وإذا قيمة التنشيط اقل من او تساوي 0.5 النموذج سوف يعتبر Y=0 او صورة شي غير قطة.



def predict(w, b, X): # دالة التنبؤ
    '''
    Predict whether the label is 0 or 1 using learned logistic regression parameters (w, b)
    
    Arguments:
    w -- weights, a numpy array of size (num_px * num_px * 3, 1) 
    b -- bias, a scalar
    X -- data of size (num_px * num_px * 3, number of examples)
    
    Returns:
    Y_prediction -- a numpy array (vector) containing all predictions (0/1) for the examples in X # بيرجعلك مصفوفة فيها كل التنبوءات لكل القيم المدخلة
    '''
    
    m = X.shape[1] #حجم البيانات
    Y_prediction = np.zeros((1,m)) 
    w = w.reshape(X.shape[0], 1) 
    
    # Compute vector "A" predicting the probabilities of a cat being present in the picture
    A = sigmoid(np.dot(w.T, X) + b) # معادلة حساب الانحدار اللوجستي لكل القيم
    
    for i in range(A.shape[1]):
        
        # Convert probabilities A[0,i] to actual predictions p[0,i]

        Y_prediction[0, i] = 1 if A[0, i] > 0.5 else 0 # تحدد يا 1 او 0 لكل عينة بالترتيب

    
    assert(Y_prediction.shape == (1, m))
    
    return Y_prediction
w = np.array([[0.1124579],[0.23106775]]) 



# تجربة 
b = -0.3
X = np.array([[1.,-1.1,-3.2],[1.2,2.,0.1]])
print ("predictions = " + str(predict(w, b, X)))

Expected Output
predictions = [[ 1.  1.  0.]]

دمج ما ذكر في نموذج واحد

الان سنبني نموذج عام نضع فيه وضع كل اللبنات معا، في الترتيب الصحيح.

def model(X_train, Y_train, X_test, Y_test, num_iterations = 2000, learning_rate = 0.5, print_cost = False): 
    """
    Builds the logistic regression model by calling the function you've implemented previously
    
    Arguments:
    X_train -- training set represented by a numpy array of shape (num_px * num_px * 3, m_train) # عينات التدريب
    Y_train -- training labels represented by a numpy array (vector) of shape (1, m_train) # تصنيفات عينات التدريب
    X_test -- test set represented by a numpy array of shape (num_px * num_px * 3, m_test) # عينات الفحص عشان تشوف جودة نموذجك
    Y_test -- test labels represented by a numpy array (vector) of shape (1, m_test) # تصنيفات عينات الفحص
    num_iterations -- hyperparameter representing the number of iterations to optimize the parameters # عدد التكرارات
    learning_rate -- hyperparameter representing the learning rate used in the update rule of optimize() # معدل التعلم
    print_cost -- Set to true to print the cost every 100 iterations # خيار طباعة التكلفة
    
    Returns:
    d -- dictionary containing information about the model. # بيرجعلك قاموس فيه معلومات المخرجات لنموذجك كاملة
    """
    # لاحظ انا نستدعي الدوال السابقة كلها بالترتيب
    
    # initialize parameters with zeros 
    w, b = initialize_with_zeros(X_train.shape[0])

    # Gradient descent 
    parameters, grads, costs = optimize(w, b, X_train, Y_train, num_iterations, learning_rate, print_cost)
    
    # Retrieve parameters w and b from dictionary "parameters"
    w = parameters["w"]
    b = parameters["b"]
    
    # Predict test/train set examples
    Y_prediction_test = predict(w, b, X_test) # جودة تعيين التصنيفات لعينات التدريب
    Y_prediction_train = predict(w, b, X_train) # جودة تعيين التصنيفات لعينات الفحص او العينات الي مايعرفها النوذج

    # Print train/test Errors
    print("train accuracy: {} %".format(100 - np.mean(np.abs(Y_prediction_train - Y_train)) * 100))
    print("test accuracy: {} %".format(100 - np.mean(np.abs(Y_prediction_test - Y_test)) * 100))

    
    d = {"costs": costs,
         "Y_prediction_test": Y_prediction_test, 
         "Y_prediction_train" : Y_prediction_train, 
         "w" : w, 
         "b" : b,
         "learning_rate" : learning_rate,
         "num_iterations": num_iterations}
    
    return d


# تجربة
d = model(train_set_x, train_set_y, test_set_x, test_set_y, num_iterations = 2000, learning_rate = 0.01, print_cost = True)

Expected Output
Cost after iteration 0: 0.693147
Cost after iteration 100: 0.823921
Cost after iteration 200: 0.418944
Cost after iteration 300: 0.617350
Cost after iteration 400: 0.522116
Cost after iteration 500: 0.387709
Cost after iteration 600: 0.236254
Cost after iteration 700: 0.154222
Cost after iteration 800: 0.135328
Cost after iteration 900: 0.124971
Cost after iteration 1000: 0.116478
Cost after iteration 1100: 0.109193
Cost after iteration 1200: 0.102804
Cost after iteration 1300: 0.097130
Cost after iteration 1400: 0.092043
Cost after iteration 1500: 0.087453
Cost after iteration 1600: 0.083286
Cost after iteration 1700: 0.079487
Cost after iteration 1800: 0.076007
Cost after iteration 1900: 0.072809
train accuracy: 99.52153110047847 %
test accuracy: 70.0 %

دقة التدريب قريبة من 100٪. هذه دلالة على ان النموذج يعمل بشكل جيد لاستيعاب بيانات التدريب. دقة الاختبار هي 68%. في الواقع ليس سيئا لهذا النموذج البسيط، نظرا لمجموعة البيانات الصغيرة التي استخدمناها وأن الانحدار اللوجستي هو مصنف خطي بسيط وليس معقد. هنالك نماذج شبكات عصبية أكثر تطورا وتعقيدا للحصول على نتائج أفضل.



سوف نجرب الان مثالين لتنبأ صحيح وخاطئ باستخدام عينات الفحص التي لم تستخدم في التدريب. 

# Example of a picture that was wrongly classified. نموذج لصورة تنبأ بها النموذج بشكل ناجح 
index = 10 #تقدر تغيره بأي رقم تحب 
plt.imshow(test_set_x[:,index].reshape((num_px, num_px, 3)))
print ("y = " + str(test_set_y[0, index]) + ", you predicted that it is a \"" + classes[d["Y_prediction_test"][0, index]].decode("utf-8") +  "\" picture.")

Expected Output
y = 1, you predicted that it is a "non-cat" picture.





# Example of a picture that was wrongly classified. نموذج لصورة تنبأ بها النموذج بشكل خاطيْ 
index = 5
plt.imshow(test_set_x[:,index].reshape((num_px, num_px, 3)))
print ("y = " + str(test_set_y[0, index]) + ", you predicted that it is a \"" + classes[d["Y_prediction_test"][0, index]].decode("utf-8") +  "\" picture.")

Expected Output
y = 0, you predicted that it is a "cat" picture.

هناك تعليق واحد:

  1. m_train = train_set_y.shape[1] #عدد أمثلة التدريب
    m_test = test_set_y.shape[1] #عدد امثلة الاختبار
    num_px = train_set_x_orig.shape[1] # عدد البكسلات
    pourqoui 1?

    ردحذف