Logo

الإعداد

7 دقائق قراءة
شرائح الدرس
1 / 13

الإعداد: الخلايا العصبية كبوابات منطقية

استخدام خلية sigmoid لتنفيذ البوابات المنطقية الأساسية، وتعلّمها، وتجاوز حدودها

import numpy as np
import tensorflow as tf
import matplotlib.pyplot as plt
import itertools as it
 
%matplotlib inline

الخلايا العصبية كبوابات منطقية

كمقدمة للشبكات العصبية ومكوّنها الأساسي (الخلية العصبية - Neuron)، سننظر في كيفية استخدام الخلايا العصبية لتنفيذ أبسط عمليات الحساب المنطقي: البوابات المنطقية (Logic Gates). لنبدأ!

دالة السيجمويد (Sigmoid Function)

الدالة التنشيطية (Activation Function) الأساسية والكلاسيكية التي نطبّقها على الخلايا العصبية هي دالة السيجمويد (تُسمى أحيانًا ببساطة دالة السيجمويد): وهي الدالة اللوجستية (Logistic Function) القياسية.

σ=11+ex\sigma = \frac{1}{1 + e^{-x}}

تتراوح قيمة σ\sigma بين (0, 1). عندما يكون المدخل xx سالبًا، تقترب σ\sigma من 0. وعندما يكون xx موجبًا، تقترب σ\sigma من 1. عند x=0x=0، تكون σ=0.5\sigma=0.5

يمكننا تنفيذ هذا بسهولة باستخدام NumPy.

def sigmoid(x):
    """Sigmoid function"""
    return 1.0 / (1.0 + np.exp(-x))

ثم نرسمها باستخدام matplotlib.

# Plot The sigmoid function
xs = np.linspace(-10, 10, num=100, dtype=np.float32)
activation = sigmoid(xs)
 
fig = plt.figure(figsize=(6,4))
plt.plot(xs, activation)
plt.plot(0,.5,'ro')
 
plt.grid(True, which='both')
plt.axhline(y=0, color='y')
plt.axvline(x=0, color='y')
plt.ylim([-0.1, 1.15])
(-0.1, 1.15)

مثال باستخدام بوابة OR

منطق بوابة OR

تأخذ البوابة المنطقية مدخلَين منطقيَّين (Boolean) (صحيح/خطأ أو 1/0)، وتُعيد إما 0 أو 1 حسب قاعدتها. يوضّح جدول الحقيقة (Truth Table) للبوابة المنطقية المخرجات لكل توليفة من المدخلات: (0, 0)، و(0, 1)، و(1, 0)، و(1, 1). على سبيل المثال، لننظر إلى جدول الحقيقة لبوابة Or:

جدول الحقيقة لبوابة OR
المدخلالمخرج
000
011
101
111
OR كخلية عصبية

الخلية العصبية التي تستخدم دالة تنشيط السيجمويد تُخرج قيمة بين (0, 1). هذا يقودنا بشكل طبيعي إلى التفكير بالقيم المنطقية (Boolean). تخيّل خلية عصبية تأخذ مدخلَين، x1x_1 وx2x_2، وحد انحياز (Bias):

بتقييد مدخلَي x1x_1 وx2x_2 لتكونا ضمن {0,1}\left\{0, 1\right\}، يمكننا محاكاة تأثير البوابات المنطقية باستخدام خليتنا العصبية. الهدف هو إيجاد الأوزان (الممثَّلة بعلامات ؟ أعلاه)، بحيث تُعيد الخلية مخرجًا قريبًا من 0 أو 1 حسب المدخلات. ما هي الأوزان التي يجب استخدامها لإخراج نفس نتائج OR؟ تذكّر: تقترب σ(z)\sigma(z) من 0 عندما تكون zz سالبة بشكل كبير (حوالي -10 أو أقل)، وتقترب من 1 عندما تكون zz موجبة بشكل كبير (حوالي +10 أو أكثر).

z=w1x1+w2x2+bz = w_1 x_1 + w_2 x_2 + b

لنفكّر في هذا خطوة بخطوة:

  • عندما يكون كلٌّ من x1x_1 وx2x_2 يساوي 0، فإن القيمة الوحيدة التي تؤثر على zz هي bb. ولأننا نريد أن تكون النتيجة للمدخل (0, 0) قريبة من الصفر، يجب أن تكون bb سالبة (على الأقل -10) للوصول إلى الجزء الأيسر من منحنى السيجمويد.
  • إذا كان أيٌّ من x1x_1 أو x2x_2 يساوي 1، نريد أن يكون المخرج قريبًا من 1. هذا يعني أن الأوزان المرتبطة بـx1x_1 وx2x_2 يجب أن تكون كافية لتعويض bb إلى درجة تجعل zz لا تقل عن 10 (أي في الجزء الأيمن البعيد من منحنى السيجمويد).

لنعطِ bb القيمة -10. ما مدى الحجم الذي نحتاجه لـw1w_1 وw2w_2؟ على الأقل +20 ستوصلنا إلى +10 عند تفعيل واحدة فقط من {w1,w2}\{w_1, w_2\}.

فلنجرّب w1=20w_1=20، وw2=20w_2=20، وb=10b=-10:

بعض الدوال المساعدة

بما أننا سنُنشئ عدة بوابات منطقية كأمثلة (من مجموعات مختلفة من الأوزان والانحيازات)، إليك دالتين مساعدتين. الأولى تأخذ أوزاننا وانحيازاتنا وتحوّلها إلى دالة بمعاملَين يمكننا استخدامها مثل and(a,b). والثانية لطباعة جدول الحقيقة لبوابة ما.

# Logic gate function
def logic_gate(w1, w2, b, x1, x2):
  ''' logic_gate is a function which returns the results of
        taking two args and  (hopefully) acts like a logic gate (and/or/not/etc.).
        its behavior is determined by w1,w2,b. '''
  return sigmoid(w1 * x1 + w2 * x2 + b)
# Test function that takes a function with two arguments
def test_gate(w1, w2, b):
  for x1 in range(2):
    for x2 in range(2):
      print("{}, {}: {}".format(x1, x2, np.round(logic_gate(w1, w2, b, x1, x2))))

لنرَ كيف كان أداؤنا. إليك جدول الحقيقة المرجعي (Gold-Standard).

جدول الحقيقة لبوابة OR
المدخلالمخرج
000
011
101
111

وإليك نتيجتنا:

or_gate = test_gate(20, 20, -10)
0, 0: 0.0
0, 1: 1.0
1, 0: 1.0
1, 1: 1.0

هذا مطابق - ممتاز!

التمرين 1

الجزء الأول: بوابة AND

الآن حاول أنت إيجاد قيم الأوزان المناسبة لكل جدول حقيقة. حاول ألا تخمّن وتتحقق فقط. فكّر بالأمر منطقيًا وحاول اشتقاق قيم تنجح.

جدول الحقيقة لبوابة AND
المدخلالمخرج
000
010
100
111
# Fill in the w1, w2, and b parameters such that the truth table matches
# and_gate = test_gate(...)
الجزء الثاني: بوابة NOR (Not Or)
جدول الحقيقة لبوابة NOR
المدخلالمخرج
001
010
100
110
# Fill in the w1, w2, and b parameters such that the truth table matches
# nor_gate = test_gate(...)
الجزء الثالث: بوابة NAND (Not And)
جدول الحقيقة لبوابة NAND
المدخلالمخرج
001
011
101
110
# Fill in the w1, w2, and b parameters such that the truth table matches
# nand_gate = test_gate(...)

حلول التمرين 1

 

تعلّم بوابة منطقية

يمكننا استخدام TensorFlow لمحاولة تعليم نموذج الأوزان والانحياز الصحيحَين من خلال تمرير جدول الحقيقة الخاص بنا كبيانات تدريب.

# Define LogicGate Model using tf.Module
class LogicGate(tf.Module):
    def __init__(self):
        super().__init__()
        self.built = False  # Track if model is initialized
 
    def __call__(self, x, train=True):
        # Initialize weights and bias on first call
        if not self.built:
            input_dim = x.shape[-1]  # Number of input features
            self.w = tf.Variable(tf.random.normal([input_dim, 1]), name="weights")
            self.b = tf.Variable(tf.zeros([1]), name="bias")
            self.built = True
 
        # Compute logits: z = Wx + b
        z = tf.add(tf.matmul(x, self.w), self.b)
        return tf.sigmoid(z)  # Apply sigmoid
 
# Loss function (Mean Squared Error)
def compute_loss(y_pred, y_true):
    return tf.reduce_mean(tf.square(y_pred - y_true))
 
# Training function
def train_model(model, x_train, y_train, learning_rate=0.5, epochs=5000):
  # Iterate over the training data
  for epoch in range(epochs):
    with tf.GradientTape() as tape:
      y_pred = model(x_train)  # Forward pass
      loss = compute_loss(y_pred, y_train)
 
    # Update the parameters with respect to the gradient calculations
    grads = tape.gradient(loss, model.variables)
    for g,v in zip(grads, model.variables):
      v.assign_sub(learning_rate * g)
 
    # Print progress every 1000 epochs
    if epoch % 1000 == 0:
      acc = compute_accuracy(model, x_train, y_train)
      print(f"Epoch {epoch}, Loss: {loss.numpy():.4f}, Accuracy: {acc:.4f}")
 
# Accuracy function
def compute_accuracy(model, x, y_true):
    y_pred = model(x, train=False)
    y_pred_rounded = tf.round(y_pred)
    correct = tf.equal(y_pred_rounded, y_true)
    return tf.reduce_mean(tf.cast(correct, tf.float32)).numpy()
 
# Prepare AND gate dataset
and_table = np.array([[0, 0, 0],
                      [1, 0, 0],
                      [0, 1, 0],
                      [1, 1, 1]], dtype=np.float32)
 
x_train = and_table[:, :2]  # Inputs: x1, x2
y_train = and_table[:, 2:]  # Labels: y
 
# Initialize and train model
model = LogicGate()
train_model(model, x_train, y_train)
 
# Evaluate and print results
w1, w2 = model.w.numpy().flatten()
b = model.b.numpy().flatten()[0]
print(f"\nLearned weight for w1: {w1}")
print(f"Learned weight for w2: {w2}")
print(f"Learned bias: {b}\n")
 
# Test model predictions
y_pred = model(x_train, train=False).numpy().round().astype(np.uint8)
print("Predicted Truth Table:")
print(np.column_stack((and_table[:, :2], y_pred)))
Epoch 0, Loss: 0.3312, Accuracy: 0.5000
Epoch 1000, Loss: 0.0126, Accuracy: 1.0000
Epoch 2000, Loss: 0.0058, Accuracy: 1.0000
Epoch 3000, Loss: 0.0037, Accuracy: 1.0000
Epoch 4000, Loss: 0.0027, Accuracy: 1.0000
 
Learned weight for w1: 5.733343124389648
Learned weight for w2: 5.733343124389648
Learned bias: -8.691651344299316
 
Predicted Truth Table:
[[0. 0. 0.]
 [1. 0. 0.]
 [0. 1. 0.]
 [1. 1. 1.]]

حدود الخلية العصبية المفردة

إذا درست مقررات علوم الحاسب، فقد تعرف أن بوابات XOR هي أساس الحوسبة. يمكن استخدامها كأنصاف جامعات (Half-Adders)، وهي أساس القدرة على جمع الأرقام معًا. إليك جدول الحقيقة لبوابة XOR:

بوابة XOR (Exclusive Or)
جدول الحقيقة لبوابة NAND
المدخلالمخرج
000
011
101
110

الآن السؤال هو: هل يمكنك إنشاء مجموعة أوزان بحيث تُخرج خلية عصبية واحدة هذه الخاصية؟ تبيّن أن هذا غير ممكن. فالخلايا العصبية المفردة لا يمكنها ربط المدخلات ببعضها، لذا فهي ببساطة "مرتبكة". إذن الخلايا الفردية مستبعَدة. فهل يمكننا مع ذلك استخدام الخلايا العصبية لتكوين بوابة XOR بطريقة ما؟

ماذا لو جرّبنا شيئًا أكثر تعقيدًا:

هنا، لدينا المدخلات تذهب إلى بوابتين منفصلتين: الخلية العلوية هي بوابة OR، والسفلية هي بوابة NAND. تُمرَّر مخرجات هاتين البوابتين إلى خلية عصبية أخرى، وهي بوابة AND. إذا حسبت المخرجات لكل توليفة من قيم المدخلات، ستلاحظ أن هذه هي بالفعل بوابة XOR!

XOR(A,B)=OR(A, B) AND NAND(A,B).

# Make sure you have or_gate, nand_gate, and and_gate working from above
#def xor_gate(a, b):
#    c = or_gate(a, b)
#    d = nand_gate(a, b)
#    return and_gate(c, d)
#test(xor_gate)

وهكذا، نرى كيف أن ربط الخلايا العصبية ببعضها سلسلةً يمكن أن يكوّن نماذج أكثر تعقيدًا مما كنا سنصل إليه بطريقة أخرى.

 

التمرين 2: تعلّم بوابة XOR

إذا ركّبنا نموذجًا من مرحلتين، يمكننا تعلّم بوابة XOR. ستلاحظ أن تعريف النموذج نفسه بدأ يصبح معقّدًا بعض الشيء. سنتحدث عن طرق التعامل مع ذلك الأسبوع القادم.

حلول التمرين 2