الإعداد
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) القياسية.
تتراوح قيمة بين (0, 1). عندما يكون المدخل سالبًا، تقترب من 0. وعندما يكون موجبًا، تقترب من 1. عند ، تكون
يمكننا تنفيذ هذا بسهولة باستخدام 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 | ||
|---|---|---|
| المدخل | المخرج | |
| 0 | 0 | 0 |
| 0 | 1 | 1 |
| 1 | 0 | 1 |
| 1 | 1 | 1 |
OR كخلية عصبية
الخلية العصبية التي تستخدم دالة تنشيط السيجمويد تُخرج قيمة بين (0, 1). هذا يقودنا بشكل طبيعي إلى التفكير بالقيم المنطقية (Boolean). تخيّل خلية عصبية تأخذ مدخلَين، و، وحد انحياز (Bias):
بتقييد مدخلَي و لتكونا ضمن ، يمكننا محاكاة تأثير البوابات المنطقية باستخدام خليتنا العصبية. الهدف هو إيجاد الأوزان (الممثَّلة بعلامات ؟ أعلاه)، بحيث تُعيد الخلية مخرجًا قريبًا من 0 أو 1 حسب المدخلات. ما هي الأوزان التي يجب استخدامها لإخراج نفس نتائج OR؟ تذكّر: تقترب من 0 عندما تكون سالبة بشكل كبير (حوالي -10 أو أقل)، وتقترب من 1 عندما تكون موجبة بشكل كبير (حوالي +10 أو أكثر).
لنفكّر في هذا خطوة بخطوة:
- عندما يكون كلٌّ من و يساوي 0، فإن القيمة الوحيدة التي تؤثر على هي . ولأننا نريد أن تكون النتيجة للمدخل (0, 0) قريبة من الصفر، يجب أن تكون سالبة (على الأقل -10) للوصول إلى الجزء الأيسر من منحنى السيجمويد.
- إذا كان أيٌّ من أو يساوي 1، نريد أن يكون المخرج قريبًا من 1. هذا يعني أن الأوزان المرتبطة بـ و يجب أن تكون كافية لتعويض إلى درجة تجعل لا تقل عن 10 (أي في الجزء الأيمن البعيد من منحنى السيجمويد).
لنعطِ القيمة -10. ما مدى الحجم الذي نحتاجه لـ و؟ على الأقل +20 ستوصلنا إلى +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 | ||
|---|---|---|
| المدخل | المخرج | |
| 0 | 0 | 0 |
| 0 | 1 | 1 |
| 1 | 0 | 1 |
| 1 | 1 | 1 |
وإليك نتيجتنا:
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 | ||
|---|---|---|
| المدخل | المخرج | |
| 0 | 0 | 0 |
| 0 | 1 | 0 |
| 1 | 0 | 0 |
| 1 | 1 | 1 |
# Fill in the w1, w2, and b parameters such that the truth table matches
# and_gate = test_gate(...)الجزء الثاني: بوابة NOR (Not Or)
| جدول الحقيقة لبوابة NOR | ||
|---|---|---|
| المدخل | المخرج | |
| 0 | 0 | 1 |
| 0 | 1 | 0 |
| 1 | 0 | 0 |
| 1 | 1 | 0 |
# Fill in the w1, w2, and b parameters such that the truth table matches
# nor_gate = test_gate(...)الجزء الثالث: بوابة NAND (Not And)
| جدول الحقيقة لبوابة NAND | ||
|---|---|---|
| المدخل | المخرج | |
| 0 | 0 | 1 |
| 0 | 1 | 1 |
| 1 | 0 | 1 |
| 1 | 1 | 0 |
# 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 | ||
|---|---|---|
| المدخل | المخرج | |
| 0 | 0 | 0 |
| 0 | 1 | 1 |
| 1 | 0 | 1 |
| 1 | 1 | 0 |
الآن السؤال هو: هل يمكنك إنشاء مجموعة أوزان بحيث تُخرج خلية عصبية واحدة هذه الخاصية؟ تبيّن أن هذا غير ممكن. فالخلايا العصبية المفردة لا يمكنها ربط المدخلات ببعضها، لذا فهي ببساطة "مرتبكة". إذن الخلايا الفردية مستبعَدة. فهل يمكننا مع ذلك استخدام الخلايا العصبية لتكوين بوابة 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