paint-brush
يعتمد التعلم العميق على الرياضيات ذات الفاصلة العائمة. ماذا لو كان هذا خطأ؟بواسطة@abhiyanampally_kob9nse8
549 قراءة٪ s
549 قراءة٪ s

يعتمد التعلم العميق على الرياضيات ذات الفاصلة العائمة. ماذا لو كان هذا خطأ؟

بواسطة 40m2025/02/11
Read on Terminal Reader

طويل جدا؛ ليقرأ

نظام الأرقام اللوغاريتمية (LNS) هو تمثيل رقمي بديل لحسابات النقطة العائمة (FP). يمثل LNS الأرقام على مقياس لوغاريتمي، ويحول الضرب إلى جمع، وهو ما قد يكون أرخص حسابيًا في بعض بنيات الأجهزة. ومع ذلك، تتطلب عمليات الجمع والطرح في LNS تقريبًا، مما يؤدي إلى تقليل الدقة. نستخدم LNS لتدريب Perceptron متعدد الطبقات البسيط والمتصل بالكامل على MNIST.
featured image - يعتمد التعلم العميق على الرياضيات ذات الفاصلة العائمة. ماذا لو كان هذا خطأ؟
undefined HackerNoon profile picture
0-item
1-item

عندما صادفت لأول مرة فكرة استخدام نظام الأرقام اللوغاريتمية (LNS) في التعلم العميق، كنت مفتونًا ولكن متشككًا أيضًا. مثل معظمنا، كنت أعمل دائمًا مع حساب النقطة العائمة (FP) - المعيار للحساب العددي في التعلم العميق. يوفر FP توازنًا جيدًا بين الدقة والنطاق، ولكنه يأتي مع مقايضات: استخدام ذاكرة أعلى، وتعقيد حسابي متزايد، واستهلاك أكبر للطاقة. لذلك، قررت التجربة ومعرفة بنفسي - كيف يقارن LNS بـ FP عند تدريب Perceptron متعدد الطبقات (MLP) البسيط المتصل بالكامل على MNIST؟

لماذا يجب أن تفكر في LNS؟

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

الخلفية: نظام الأعداد العشرية العائمة مقابل نظام الأعداد اللوغاريتمية

تمثيل النقطة العائمة (FP)

الحساب ذو النقطة العائمة هو التمثيل العددي القياسي في معظم أطر التعلم العميق، مثل PyTorch و TensorFlow. تحتوي أرقام النقطة العائمة على:


  • بت الإشارة (تحديد القيمة الإيجابية أو السلبية)
  • الأس (عامل القياس)
  • جزء عشري (دلالة) (دقة العدد)


تُستخدم FP32 (الدقة الفردية) بشكل شائع في التعلم العميق، حيث توفر التوازن بين الدقة العددية والكفاءة الحسابية. تكتسب التنسيقات الأكثر كفاءة مثل FP16 وBF16 شعبية كبيرة لتسريع التدريب.

نظام الأعداد اللوغاريتمية (LNS)

LNS هو تمثيل رقمي بديل حيث يتم تخزين الأرقام على هيئة لوغاريتمات: [x = \log_b (y)] حيث (b) هي قاعدة اللوغاريتم. يتمتع LNS بالعديد من المزايا:


  • يتم تبسيط عملية الضرب إلى عملية جمع : ( x_1 * x_2 = b^{(\log_b x_1 + \log_b x_2)} )
  • يتم تبسيط القسمة إلى الطرح : ( x_1 / x_2 = b^{(\log_b x_1 - \log_b x_2)} )
  • تصبح وظائف النمو الأسي خطية


ومع ذلك، تتطلب عمليات الجمع والطرح في LNS تقريبات، مما يؤدي إلى انخفاض الدقة.

عمليات حسابية LNS

لاستكشاف LNS بشكل أكبر، قمت بتنفيذ عمليات حسابية أساسية مثل الجمع والطرح والضرب والقسمة باستخدام التمثيلات الداخلية لـ LNS.


 import torch import numpy as np import xlns as xl # Assuming xlns module is installed and provides xlnsnp # Function to convert floating-point numbers to xlns internal representation def float_to_internal(arr): xlns_data = xl.xlnsnp(arr) return xlns_data.nd # Function to convert xlns internal representation back to floating-point numbers def internal_to_float(internal_data): original_numbers = [] for value in internal_data: x = value // 2 s = value % 2 # Use x and s to create xlns object xlns_value = xl.xlns(0) xlns_value.x = x xlns_value.s = s original_numbers.append(float(xlns_value)) return original_numbers # Function to perform LNS addition using internal representation def lns_add_internal(x, y): max_part = torch.maximum(x, y) diff = torch.abs(x - y) adjust_term = torch.log1p(torch.exp(-diff)) return max_part + adjust_term # Function to perform LNS subtraction using internal representation def lns_sub_internal(x, y): return lns_add_internal(x, -y) # Function to perform LNS multiplication using internal representation def lns_mul_internal(x, y): return x + y # Function to perform LNS division using internal representation def lns_div_internal(x, y): return x - y # Input floating-point arrays x_float = [2.0, 3.0] y_float = [-1.0, 0.0] # Convert floating-point arrays to xlns internal representation x_internal = float_to_internal(x_float) y_internal = float_to_internal(y_float) # Create tensors from the internal representation tensor_x_nd = torch.tensor(x_internal, dtype=torch.int64) tensor_y_nd = torch.tensor(y_internal, dtype=torch.int64) # Perform the toy LNS addition on the internal representation result_add_internal = lns_add_internal(tensor_x_nd, tensor_y_nd) # Perform the toy LNS subtraction on the internal representation result_sub_internal = lns_sub_internal(tensor_x_nd, tensor_y_nd) # Perform the toy LNS multiplication on the internal representation result_mul_internal = lns_mul_internal(tensor_x_nd, tensor_y_nd) # Perform the toy LNS division on the internal representation result_div_internal = lns_div_internal(tensor_x_nd, tensor_y_nd) # Convert the internal results back to original floating-point values result_add_float = internal_to_float(result_add_internal.numpy()) result_sub_float = internal_to_float(result_sub_internal.numpy()) result_mul_float = internal_to_float(result_mul_internal.numpy()) result_div_float = internal_to_float(result_div_internal.numpy()) # Convert the results back to PyTorch tensors result_add_tensor = torch.tensor(result_add_float, dtype=torch.float32) result_sub_tensor = torch.tensor(result_sub_float, dtype=torch.float32) result_mul_tensor = torch.tensor(result_mul_float, dtype=torch.float32) result_div_tensor = torch.tensor(result_div_float, dtype=torch.float32) # Print results print("Input x:", x_float) print("Input y:", y_float) print("Addition Result:", result_add_float) print("Addition Result Tensor:", result_add_tensor) print("Subtraction Result:", result_sub_float) print("Subtraction Result Tensor:", result_sub_tensor) print("Multiplication Result:", result_mul_float) print("Multiplication Result Tensor:", result_mul_tensor) print("Division Result:", result_div_float) print("Division Result Tensor:", result_div_tensor)


فيما يلي تفصيل لتطبيقاتي التجريبية لنظام الأرقام اللوغاريتمية (LNS).

1. مفهوم LNS الأساسي والتحديات في PyTorch

في LNS، يتم تمثيل الأرقام على هيئة لوغاريتمات، مما يحول الضرب والقسمة إلى جمع وطرح. ومع ذلك، فإن تنفيذ هذا باستخدام PyTorch يمثل تحديات معينة نظرًا لأن موتر PyTorch يستخدم تمثيلات الفاصلة العائمة داخليًا. وهذا يفرض العديد من المتطلبات:


  • الحفاظ على التمثيل اللوغاريتمي خلال العمليات الحسابية.
  • ضمان الاستقرار العددي.
  • تعامل مع التحويلات بعناية.
  • إدارة التمثيل الداخلي باستخدام مكونين:
    • x : القيمة اللوغاريتمية.
    • s : بت الإشارة (0 أو 1).

2. التمثيل الداخلي والتحويل

الخطوة الأولى هي تحويل الأرقام ذات الفاصلة العائمة إلى تمثيلها الداخلي LNS.

 import torch import numpy as np import xl # Hypothetical external LNS library def float_to_internal(arr): xlns_data = xl.xlnsnp(arr) return xlns_data.nd # Convert floating-point arrays to xlns internal representation x_float = np.array([2.0, 3.0]) y_float = np.array([-1.0, 0.0]) x_internal = float_to_internal(x_float) y_internal = float_to_internal(y_float) # Create tensors from the internal representation tensor_x_nd = torch.tensor(x_internal, dtype=torch.int64) tensor_y_nd = torch.tensor(y_internal, dtype=torch.int64)


يعد استخدام dtype=torch.int64 أمرًا بالغ الأهمية لأنه:

  • يحافظ على التمثيل الداخلي الدقيق لـ LNS دون أخطاء التقريب ذات الفاصلة العائمة.
  • يقوم بتجميع كل من القيمة اللوغاريتمية وبت الإشارة في عدد صحيح واحد.
  • يمنع عمليات النقطة العائمة غير المقصودة من إتلاف تمثيل LNS.

3. العمليات الحسابية الأساسية

أ) الضرب

 def lns_mul_internal(x, y): return x + y

الضرب في LNS يصبح جمعًا:

  • إذا كان a = log(x) و b = log(y) ، فإن log(x×y) = log(x) + log(y) .

ب) القسمة

 def lns_div_internal(x, y): return x - y

القسمة تصبح طرحًا:

  • log(x/y) = log(x) - log(y) .

ج) الإضافة

 def lns_add_internal(x, y): max_part = torch.maximum(x, y) diff = torch.abs(x - y) adjust_term = torch.log1p(torch.exp(-diff)) return max_part + adjust_term


تعتبر عملية الجمع أكثر تعقيدًا وحساسية عددية لأن:

  • فهو يتضمن عمليات أسيّة ولوغاريتمية.
  • قد يؤدي التنفيذ المباشر للفاصلة العائمة إلى حدوث فيضان/فيضان ناقص.
  • يستخدم المعادلة: log(x + y) = log(max(x,y)) + log(1 + exp(log(min(x,y)) - log(max(x,y)))) .
  • يستخدم log1p بدلاً من log(1 + x) المباشر لتحقيق استقرار عددي أفضل.

4. سلامة النوع وإدارة التحويل

 def internal_to_float(internal_data): for value in internal_data: x = value // 2 # Integer division s = value % 2 # Integer modulo


يحافظ خط أنابيب التحويل على فصل واضح:

  1. التحويل من float → التمثيل الداخلي LNS (الأعداد الصحيحة).
  2. تنفيذ عمليات LNS باستخدام الحساب الصحيح.
  3. تحويل مرة أخرى إلى التعويم فقط عند الضرورة.
 # Convert results back to float and tensor result_add_float = internal_to_float(result_add_internal.numpy()) result_add_tensor = torch.tensor(result_add_float, dtype=torch.float32)

5. المزايا والقيود الرئيسية

المزايا

  • يتم تبسيط عملية الضرب والقسمة إلى الجمع والطرح.
  • نطاق ديناميكي واسع مع حسابيات النقطة الثابتة.
  • من المحتمل أن يكون أكثر كفاءة في تطبيقات معينة.

القيود

  • الجمع والطرح عمليات أكثر تعقيدًا .
  • تكلفة التحويل بين النقطة العائمة و LNS.
  • يتطلب معالجة خاصة للأرقام الصفرية والسلبية.
  • يتطلب توافق موتر PyTorch إدارة دقيقة للنوع.

6. إمكانيات التحسين

لتحسين الأداء، يمكن القيام بما يلي:

  1. تنفيذ وظيفة Autograd مخصصة لـ PyTorch لعمليات LNS.
  2. إنشاء نوع موتر مخصص يدعم LNS بشكل أصلي.
  3. استخدم أنوية CUDA لعمليات LNS فعالة على وحدة معالجة الرسومات.


إن التنفيذ الحالي يجعل من الممكن تحقيق مقايضات عملية:

  • إعطاء الأولوية للوضوح وإمكانية الصيانة على الأداء الأقصى.
  • يستخدم البنية التحتية لموتر PyTorch الحالي مع الحفاظ على دقة LNS.
  • يحافظ على الاستقرار العددي من خلال إدارة النوع الدقيقة.
  • يقلل التحويلات بين التمثيلات .

7. مثال على تدفق البيانات

توضح الخطوات التالية خط الأنابيب الكامل باستخدام قيم المثال [2.0, 3.0] و [-1.0, 0.0] :

  1. تحويل العناصر العائمة المدخلة إلى تمثيل داخلي لـ LNS.
  2. إنشاء متجهات عددية صحيحة لتخزين قيم LNS.
  3. إجراء عمليات حسابية في مجال LNS.
  4. تحويل النتائج مرة أخرى إلى الفاصلة العائمة.
  5. إنشاء موتر PyTorch النهائي لمزيد من المعالجة.


نجح هذا التنفيذ في سد الفجوة بين نظام موتر النقطة العائمة الخاص بـ PyTorch وحسابات LNS مع الحفاظ على الاستقرار العددي والدقة.


تدريب MLP متصل بالكامل على مجموعة بيانات MNIST Digit باستخدام FP وLNS

إعداد التجربة

لقد قمت بتدريب MLP متصل بالكامل على مجموعة بيانات MNIST باستخدام كل من تمثيلات FP وLNS. كانت بنية النموذج بسيطة:

  • طبقة الإدخال: 784 خلية عصبية (صور مسطحة بحجم 28 × 28)
  • الطبقات المخفية: طبقتان تحتويان على 256 و128 خلية عصبية، وتنشيطات ReLU
  • طبقة الإخراج: 10 عصبونات (واحدة لكل رقم، باستخدام سوفت ماكس)
  • دالة الخسارة: الإنتروبيا المتقاطعة
  • المُحسِّن: آدم


بالنسبة لتنفيذ LNS، كان عليّ أن أخرج عن سير العمل المعتاد في PyTorch. على عكس FP، الذي يدعمه PyTorch بشكل أصلي، لا يوفر PyTorch عمليات LNS مدمجة. لقد وجدت مشروعًا على GitHub يسمى xlns ، والذي ينفذ تمثيلات الأرقام اللوغاريتمية والحساب، مما يجعل من الممكن استخدام LNS في الشبكات العصبية.

MLP ذات النقطة العائمة في PyTorch

نبدأ بتنفيذ MLP متصل بالكامل يعتمد على FP باستخدام PyTorch:

 import torch import torch.nn as nn import torch.optim as optim import torchvision import torchvision.transforms as transforms import matplotlib.pyplot as plt import numpy as np import time # For calculating elapsed time # Define the multi-layer perceptron (MLP) model with one hidden layer class MLP(nn.Module): def __init__(self): super(MLP, self).__init__() # Input: 28*28 features; Hidden layer: 100 neurons; Output layer: 10 neurons self.fc1 = nn.Linear(28 * 28, 100) self.relu = nn.ReLU() self.fc2 = nn.Linear(100, 10) self.logsoftmax = nn.LogSoftmax(dim=1) # For stable outputs with NLLLoss def forward(self, x): # Flatten image: (batch_size, 1, 28, 28) -> (batch_size, 784) x = x.view(x.size(0), -1) x = self.fc1(x) x = self.relu(x) x = self.fc2(x) return self.logsoftmax(x) def train_and_validate(num_epochs=5, batch_size=64, learning_rate=0.01, split_ratio=0.8): # Set the device to GPU if available device = torch.device("cuda" if torch.cuda.is_available() else "cpu") print(f"Training on device: {device}") # Transformation for MNIST: convert to tensor and normalize transform = transforms.Compose([ transforms.ToTensor(), transforms.Normalize((0.5,), (0.5,)) ]) # Load the MNIST training dataset train_dataset_full = torchvision.datasets.MNIST( root='./data', train=True, transform=transform, download=True ) # Split the dataset into training and validation sets n_total = len(train_dataset_full) n_train = int(split_ratio * n_total) n_val = n_total - n_train train_dataset, val_dataset = torch.utils.data.random_split(train_dataset_full, [n_train, n_val]) # Create DataLoaders for training and validation datasets train_loader = torch.utils.data.DataLoader(train_dataset, batch_size=batch_size, shuffle=True) val_loader = torch.utils.data.DataLoader(val_dataset, batch_size=batch_size, shuffle=False) # Initialize the model, loss function, and optimizer; move model to device model = MLP().to(device) criterion = nn.NLLLoss() optimizer = optim.SGD(model.parameters(), lr=learning_rate) # Lists to store training and validation accuracies for each epoch train_accuracies = [] val_accuracies = [] # Record the start time for measuring elapsed time start_time = time.time() # Training loop for epoch in range(num_epochs): model.train() running_loss = 0.0 correct_train = 0 total_train = 0 for inputs, labels in train_loader: # Move inputs and labels to device inputs, labels = inputs.to(device), labels.to(device) optimizer.zero_grad() outputs = model(inputs) loss = criterion(outputs, labels) loss.backward() optimizer.step() # Compute running loss and training accuracy running_loss += loss.item() * inputs.size(0) _, predicted = torch.max(outputs.data, 1) total_train += labels.size(0) correct_train += (predicted == labels).sum().item() train_accuracy = 100.0 * correct_train / total_train train_accuracies.append(train_accuracy) # Evaluate on validation set model.eval() correct_val = 0 total_val = 0 with torch.no_grad(): for inputs, labels in val_loader: inputs, labels = inputs.to(device), labels.to(device) outputs = model(inputs) _, predicted = torch.max(outputs.data, 1) total_val += labels.size(0) correct_val += (predicted == labels).sum().item() val_accuracy = 100.0 * correct_val / total_val val_accuracies.append(val_accuracy) print(f"Epoch [{epoch+1}/{num_epochs}], Loss: {running_loss/total_train:.4f}, " f"Train Acc: {train_accuracy:.2f}%, Val Acc: {val_accuracy:.2f}%") # Calculate elapsed time elapsed_time = time.time() - start_time print(f"Training completed in {elapsed_time:.2f} seconds.") # Show sample predictions from the validation set show_predictions(model, val_loader, device) # Optional: plot training and validation accuracies epochs_arr = np.arange(1, num_epochs + 1) plt.figure(figsize=(10, 6)) plt.plot(epochs_arr, train_accuracies, label='Training Accuracy', marker='o') plt.plot(epochs_arr, val_accuracies, label='Validation Accuracy', marker='x') plt.xlabel('Epoch') plt.ylabel('Accuracy (%)') plt.title('Training and Validation Accuracies') plt.legend() plt.grid(True) plt.savefig('pytorch_accuracy.png') plt.show() def show_predictions(model, data_loader, device, num_images=6): """ Displays a few sample images from the data_loader along with the model's predictions. """ model.eval() images_shown = 0 plt.figure(figsize=(12, 8)) # Get one batch of images from the validation dataset for inputs, labels in data_loader: inputs, labels = inputs.to(device), labels.to(device) with torch.no_grad(): outputs = model(inputs) _, predicted = torch.max(outputs, 1) # Loop through the batch and plot images for i in range(inputs.size(0)): if images_shown >= num_images: break # Move the image to cpu and convert to numpy for plotting img = inputs[i].cpu().squeeze() plt.subplot(2, num_images // 2, images_shown + 1) plt.imshow(img, cmap='gray') plt.title(f"Pred: {predicted[i].item()}") plt.axis('off') images_shown += 1 if images_shown >= num_images: break plt.suptitle("Sample Predictions from the Validation Set") plt.tight_layout() plt.show() if __name__ == '__main__': train_and_validate(num_epochs=5, batch_size=64, learning_rate=0.01, split_ratio=0.8)


يتبع هذا التنفيذ خط أنابيب التعلم العميق التقليدي حيث يتم التعامل مع عمليات الضرب والجمع بواسطة العمليات الحسابية FP.


فيما يلي شرح تفصيلي لهذا التنفيذ الخاص بـ PyTorch لـ Multi-Layer Perceptron (MLP) لمجموعة بيانات MNIST.

  1. هندسة النموذج (فئة MLP):
 class MLP(nn.Module): def __init__(self): super(MLP, self).__init__() self.fc1 = nn.Linear(28 * 28, 100) # First fully connected layer self.relu = nn.ReLU() # Activation function self.fc2 = nn.Linear(100, 10) # Output layer self.logsoftmax = nn.LogSoftmax(dim=1)
  1. تمريرة أمامية:
 def forward(self, x): x = x.view(x.size(0), -1) # Flatten: (batch_size, 1, 28, 28) -> (batch_size, 784) x = self.fc1(x) # First layer x = self.relu(x) # Activation x = self.fc2(x) # Output layer return self.logsoftmax(x) # Final activation
  1. إعداد التدريب:
 def train_and_validate(num_epochs=5, batch_size=64, learning_rate=0.01, split_ratio=0.8): device = torch.device("cuda" if torch.cuda.is_available() else "cpu") # Data preprocessing transform = transforms.Compose([ transforms.ToTensor(), transforms.Normalize((0.5,), (0.5,)) # Normalize to [-1, 1] ])

المكونات الرئيسية:

  • دعم وحدة معالجة الرسوميات من خلال اختيار الجهاز

  • تطبيع البيانات من أجل تدريب أفضل

  • المعلمات الفائقة القابلة للتكوين


  1. إدارة مجموعة البيانات:
 train_dataset_full = torchvision.datasets.MNIST( root='./data', train=True, transform=transform, download=True ) # Split into train/validation n_train = int(split_ratio * n_total) train_dataset, val_dataset = torch.utils.data.random_split(train_dataset_full, [n_train, n_val])
  • تنزيل مجموعة بيانات MNIST إذا لم تكن موجودة

  • تقسيم البيانات إلى مجموعات التدريب (80%) والتحقق (20%)


  1. حلقة التدريب:
 for epoch in range(num_epochs): model.train() for inputs, labels in train_loader: inputs, labels = inputs.to(device), labels.to(device) optimizer.zero_grad() # Clear gradients outputs = model(inputs) # Forward pass loss = criterion(outputs, labels)# Calculate loss loss.backward() # Backward pass optimizer.step() # Update weights

إجراءات التدريب الكلاسيكية:

  • التدرجات الصفرية

  • تمريرة للأمام

  • حساب الخسارة

  • تمريرة للخلف

  • تحديثات الوزن


  1. تصديق:
 model.eval() with torch.no_grad(): for inputs, labels in val_loader: outputs = model(inputs) _, predicted = torch.max(outputs.data, 1) total_val += labels.size(0) correct_val += (predicted == labels).sum().item()

المميزات الرئيسية:

  • تم تعيين النموذج على وضع التقييم

  • لا حاجة لحساب التدرج

  • حساب الدقة


  1. التصور:
 def show_predictions(model, data_loader, device, num_images=6): model.eval() plt.figure(figsize=(12, 8)) # Display predictions vs actual labels
  • يعرض تنبؤات العينة من مجموعة التحقق

  • مفيد للتقييم النوعي


  1. تتبع الأداء:
 # Training metrics train_accuracies.append(train_accuracy) val_accuracies.append(val_accuracy) # Plot learning curves plt.plot(epochs_arr, train_accuracies, label='Training Accuracy') plt.plot(epochs_arr, val_accuracies, label='Validation Accuracy')
  • تتبع دقة التدريب والتحقق

  • رسم منحنى التعلم

  • قياس وقت التدريب


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

نظام الأرقام اللوغاريتمية (LNS) MLP

بالنسبة لـ LNS، نحتاج إلى استخدام مكتبة xlns . على عكس FP، يستبدل LNS العمليات التي تعتمد على الضرب بشكل كبير بالجمع في المجال اللوغاريتمي. ومع ذلك، لا يدعم PyTorch هذا بشكل أصلي، لذا يتعين علينا تطبيق عمليات LNS يدويًا حيثما كان ذلك مناسبًا.

 import numpy as np import matplotlib.pyplot as plt import os import time import argparse import xlns as xl from tensorflow.keras.datasets import mnist # Use Keras's MNIST loader # If you are using fractional normalized LNS, make sure the following are uncommented import xlnsconf.xlnsudFracnorm xlnsconf.xlnsudFracnorm.ilog2 = xlnsconf.xlnsudFracnorm.ipallog2 xlnsconf.xlnsudFracnorm.ipow2 = xlnsconf.xlnsudFracnorm.ipalpow2 # Set global parameter in xlns xl.xlnssetF(10) def softmax(inp): max_vals = inp.max(axis=1) max_vals = xl.reshape(max_vals, (xl.size(max_vals), 1)) u = xl.exp(inp - max_vals) v = u.sum(axis=1) v = v.reshape((xl.size(v), 1)) u = u / v return u def main(main_params): print("arbitrary base np LNS. Also xl.hstack, xl routines in softmax") print("testing new softmax and * instead of @ for delta") print("works with type " + main_params['type']) is_training = bool(main_params['is_training']) leaking_coeff = float(main_params['leaking_coeff']) batchsize = int(main_params['minibatch_size']) lr = float(main_params['learning_rate']) num_epoch = int(main_params['num_epoch']) _lambda = float(main_params['lambda']) ones = np.array(list(np.ones((batchsize, 1)))) if is_training: # Load the MNIST dataset from Keras (x_train, y_train), (x_test, y_test) = mnist.load_data() # Normalize images to [0, 1] x_train = x_train.astype(np.float64) / 255.0 x_test = x_test.astype(np.float64) / 255.0 # One-hot encode the labels (assume 10 classes for MNIST digits 0-9) num_classes = 10 y_train = np.eye(num_classes)[y_train] y_test = np.eye(num_classes)[y_test] # Flatten the images from (28, 28) to (784,) x_train = x_train.reshape(x_train.shape[0], -1) x_test = x_test.reshape(x_test.shape[0], -1) # Use a portion of the training data for validation (the 'split' index) split = int(main_params['split']) x_val = x_train[split:] y_val = y_train[split:] x_train = x_train[:split] y_train = y_train[:split] # If available, load pretrained weights; otherwise, initialize new random weights. if os.path.isfile("./weightin.npz"): print("using ./weightin.npz") randfile = np.load("./weightin.npz", "r") W1 = randfile["W1"] W2 = randfile["W2"] randfile.close() else: print("using new random weights") # Note: The input layer now has 785 neurons (784 features + 1 bias). W1 = np.array(list(np.random.normal(0, 0.1, (785, 100)))) # The first hidden layer has 100 neurons; add bias so 101 W2 = np.array(list(np.random.normal(0, 0.1, (101, 10)))) np.savez_compressed("./weightout.npz", W1=W1, W2=W2) delta_W1 = np.array(list(np.zeros(W1.shape))) delta_W2 = np.array(list(np.zeros(W2.shape))) # Convert weights to desired type (xlns variants or float) if main_params['type'] == 'xlnsnp': lnsW1 = xl.xlnsnp(np.array(xl.xlnscopy(list(W1)))) lnsW2 = xl.xlnsnp(np.array(xl.xlnscopy(list(W2)))) lnsones = xl.xlnsnp(np.array(xl.xlnscopy(list(np.ones((batchsize, 1)))))) lnsdelta_W1 = xl.xlnsnp(np.array(xl.xlnscopy(list(np.zeros(W1.shape))))) lnsdelta_W2 = xl.xlnsnp(np.array(xl.xlnscopy(list(np.zeros(W2.shape))))) elif main_params['type'] == 'xlnsnpv': lnsW1 = xl.xlnsnpv(np.array(xl.xlnscopy(list(W1))), 6) lnsW2 = xl.xlnsnpv(np.array(xl.xlnscopy(list(W2))), 6) lnsones = xl.xlnsnpv(np.array(xl.xlnscopy(list(np.ones((batchsize, 1)))))) lnsdelta_W1 = xl.xlnsnpv(np.array(xl.xlnscopy(list(np.zeros(W1.shape))))) lnsdelta_W2 = xl.xlnsnpv(np.array(xl.xlnscopy(list(np.zeros(W2.shape))))) elif main_params['type'] == 'xlnsnpb': lnsW1 = xl.xlnsnpb(np.array(xl.xlnscopy(list(W1))), 2**2**-6) lnsW2 = xl.xlnsnpb(np.array(xl.xlnscopy(list(W2))), 2**2**-6) lnsones = xl.xlnsnpb(np.array(xl.xlnscopy(list(np.ones((batchsize, 1))))), 2**2**-xl.xlnsF) lnsdelta_W1 = xl.xlnsnpb(np.array(xl.xlnscopy(list(np.zeros(W1.shape)))), 2**2**-xl.xlnsF) lnsdelta_W2 = xl.xlnsnpb(np.array(xl.xlnscopy(list(np.zeros(W2.shape)))), 2**2**-xl.xlnsF) elif main_params['type'] == 'xlns': lnsW1 = np.array(xl.xlnscopy(list(W1))) lnsW2 = np.array(xl.xlnscopy(list(W2))) lnsones = np.array(xl.xlnscopy(list(np.ones((batchsize, 1))))) lnsdelta_W1 = np.array(xl.xlnscopy(list(np.zeros(W1.shape)))) lnsdelta_W2 = np.array(xl.xlnscopy(list(np.zeros(W2.shape)))) elif main_params['type'] == 'xlnsud': lnsW1 = np.array(xl.xlnscopy(list(W1), xl.xlnsud)) lnsW2 = np.array(xl.xlnscopy(list(W2), xl.xlnsud)) lnsones = np.array(xl.xlnscopy(list(np.ones((batchsize, 1))), xl.xlnsud)) lnsdelta_W1 = np.array(xl.xlnscopy(list(np.zeros(W1.shape)), xl.xlnsud)) lnsdelta_W2 = np.array(xl.xlnscopy(list(np.zeros(W2.shape)), xl.xlnsud)) elif main_params['type'] == 'xlnsv': lnsW1 = np.array(xl.xlnscopy(list(W1), xl.xlnsv, 6)) lnsW2 = np.array(xl.xlnscopy(list(W2), xl.xlnsv, 6)) lnsones = np.array(xl.xlnscopy(list(np.ones((batchsize, 1))), xl.xlnsv)) lnsdelta_W1 = np.array(xl.xlnscopy(list(np.zeros(W1.shape)), xl.xlnsv)) lnsdelta_W2 = np.array(xl.xlnscopy(list(np.zeros(W2.shape)), xl.xlnsv)) elif main_params['type'] == 'xlnsb': lnsW1 = np.array(xl.xlnscopy(list(W1), xl.xlnsb, 2**2**-6)) lnsW2 = np.array(xl.xlnscopy(list(W2), xl.xlnsb, 2**2**-6)) lnsones = np.array(xl.xlnscopy(list(np.ones((batchsize, 1))), xl.xlnsb, 2**2**-xl.xlnsF)) lnsdelta_W1 = np.array(xl.xlnscopy(list(np.zeros(W1.shape)), xl.xlnsb, 2**2**-xl.xlnsF)) lnsdelta_W2 = np.array(xl.xlnscopy(list(np.zeros(W2.shape)), xl.xlnsb, 2**2**-xl.xlnsF)) elif main_params['type'] == 'float': lnsW1 = np.array(list(W1)) lnsW2 = np.array(list(W2)) lnsones = np.array(list(np.ones((batchsize, 1)))) lnsdelta_W1 = np.array(list(np.zeros(W1.shape))) lnsdelta_W2 = np.array(list(np.zeros(W2.shape))) performance = {} performance['lnsacc_train'] = np.zeros(num_epoch) performance['lnsacc_val'] = np.zeros(num_epoch) start_time = time.process_time() # Training loop for epoch in range(num_epoch): print('At Epoch %d:' % (1 + epoch)) # Loop through training batches for mbatch in range(int(split / batchsize)): start = mbatch * batchsize x = np.array(x_train[start:(start + batchsize)]) y = np.array(y_train[start:(start + batchsize)]) # At this point, each x is already flattened (batchsize x 784) # Conversion based on type if main_params['type'] == 'xlnsnp': lnsx = xl.xlnsnp(np.array(xl.xlnscopy(np.array(x, dtype=np.float64)))) lnsy = xl.xlnsnp(np.array(xl.xlnscopy(np.array(y, dtype=np.float64)))) elif main_params['type'] == 'xlnsnpv': lnsx = xl.xlnsnpv(np.array(xl.xlnscopy(np.array(x, dtype=np.float64)))) lnsy = xl.xlnsnpv(np.array(xl.xlnscopy(np.array(y, dtype=np.float64)))) elif main_params['type'] == 'xlnsnpb': lnsx = xl.xlnsnpb(np.array(xl.xlnscopy(np.array(x, dtype=np.float64))), 2**2**-xl.xlnsF) lnsy = xl.xlnsnpb(np.array(xl.xlnscopy(np.array(y, dtype=np.float64))), 2**2**-xl.xlnsF) elif main_params['type'] == 'xlns': lnsx = np.array(xl.xlnscopy(np.array(x, dtype=np.float64))) lnsy = np.array(xl.xlnscopy(np.array(y, dtype=np.float64))) elif main_params['type'] == 'xlnsud': lnsx = np.array(xl.xlnscopy(np.array(x, dtype=np.float64), xl.xlnsud)) lnsy = np.array(xl.xlnscopy(np.array(y, dtype=np.float64), xl.xlnsud)) elif main_params['type'] == 'xlnsv': lnsx = np.array(xl.xlnscopy(np.array(x, dtype=np.float64), xl.xlnsv)) lnsy = np.array(xl.xlnscopy(np.array(y, dtype=np.float64), xl.xlnsv)) elif main_params['type'] == 'xlnsb': lnsx = np.array(xl.xlnscopy(np.array(x, dtype=np.float64), xl.xlnsv, 2**2**-xl.xlnsF)) lnsy = np.array(xl.xlnscopy(np.array(y, dtype=np.float64), xl.xlnsv, 2**2**-xl.xlnsF)) elif main_params['type'] == 'float': lnsx = np.array(x, dtype=np.float64) lnsy = np.array(y, dtype=np.float64) # Concatenate the bias "ones" with input features for the first layer lnss1 = xl.hstack((lnsones, lnsx)) @ lnsW1 lnsmask = (lnss1 > 0) + (leaking_coeff * (lnss1 < 0)) lnsa1 = lnss1 * lnsmask lnss2 = xl.hstack((lnsones, lnsa1)) @ lnsW2 lnsa2 = softmax(lnss2) lnsgrad_s2 = (lnsa2 - lnsy) / batchsize lnsgrad_a1 = lnsgrad_s2 @ xl.transpose(lnsW2[1:]) lnsdelta_W2 = xl.transpose(xl.hstack((lnsones, lnsa1))) * lnsgrad_s2 lnsgrad_s1 = lnsmask * lnsgrad_a1 lnsdelta_W1 = xl.transpose(xl.hstack((lnsones, lnsx))) * lnsgrad_s1 lnsW2 -= (lr * (lnsdelta_W2 + (_lambda * lnsW2))) lnsW1 -= (lr * (lnsdelta_W1 + (_lambda * lnsW1))) print('#= ', split, ' batch=', batchsize, ' lr=', lr) lnscorrect_count = 0 # Evaluate accuracy on training set for mbatch in range(int(split / batchsize)): start = mbatch * batchsize x = x_train[start:(start + batchsize)] y = y_train[start:(start + batchsize)] if main_params['type'] == 'xlnsnp': lnsx = xl.xlnsnp(np.array(xl.xlnscopy(np.array(x, dtype=np.float64)))) elif main_params['type'] == 'xlnsnpv': lnsx = xl.xlnsnpv(np.array(xl.xlnscopy(np.array(x, dtype=np.float64)))) elif main_params['type'] == 'xlnsnpb': lnsx = xl.xlnsnpb(np.array(xl.xlnscopy(np.array(x, dtype=np.float64))), 2**2**-xl.xlnsF) elif main_params['type'] == 'xlns': lnsx = np.array(xl.xlnscopy(np.array(x, dtype=np.float64))) elif main_params['type'] == 'xlnsud': lnsx = np.array(xl.xlnscopy(np.array(x, dtype=np.float64), xl.xlnsud)) elif main_params['type'] == 'xlnsv': lnsx = np.array(xl.xlnscopy(np.array(x, dtype=np.float64), xl.xlnsv)) elif main_params['type'] == 'xlnsb': lnsx = np.array(xl.xlnscopy(np.array(x, dtype=np.float64), xl.xlnsv, 2**2**-xl.xlnsF)) elif main_params['type'] == 'float': lnsx = np.array(x, dtype=np.float64) lnss1 = xl.hstack((lnsones, lnsx)) @ lnsW1 lnsmask = (lnss1 > 0) + (leaking_coeff * (lnss1 < 0)) lnsa1 = lnss1 * lnsmask lnss2 = xl.hstack((lnsones, lnsa1)) @ lnsW2 lnscorrect_count += np.sum(np.argmax(y, axis=1) == xl.argmax(lnss2, axis=1)) lnsaccuracy = lnscorrect_count / split print("train-set accuracy at epoch %d: %f" % (1 + epoch, lnsaccuracy)) performance['lnsacc_train'][epoch] = 100 * lnsaccuracy lnscorrect_count = 0 # Evaluate on the validation set for mbatch in range(int(split / batchsize)): start = mbatch * batchsize x = x_val[start:(start + batchsize)] y = y_val[start:(start + batchsize)] if main_params['type'] == 'xlnsnp': lnsx = xl.xlnsnp(np.array(xl.xlnscopy(np.array(x, dtype=np.float64)))) elif main_params['type'] == 'xlnsnpv': lnsx = xl.xlnsnpv(np.array(xl.xlnscopy(np.array(x, dtype=np.float64)))) elif main_params['type'] == 'xlnsnpb': lnsx = xl.xlnsnpb(np.array(xl.xlnscopy(np.array(x, dtype=np.float64))), 2**2**-xl.xlnsF) elif main_params['type'] == 'xlns': lnsx = np.array(xl.xlnscopy(np.array(x, dtype=np.float64))) elif main_params['type'] == 'xlnsud': lnsx = np.array(xl.xlnscopy(np.array(x, dtype=np.float64), xl.xlnsud)) elif main_params['type'] == 'xlnsv': lnsx = np.array(xl.xlnscopy(np.array(x, dtype=np.float64), xl.xlnsv)) elif main_params['type'] == 'xlnsb': lnsx = np.array(xl.xlnscopy(np.array(x, dtype=np.float64), xl.xlnsv, 2**2**-xl.xlnsF)) elif main_params['type'] == 'float': lnsx = np.array(x, dtype=np.float64) lnss1 = xl.hstack((lnsones, lnsx)) @ lnsW1 lnsmask = (lnss1 > 0) + (leaking_coeff * (lnss1 < 0)) lnsa1 = lnss1 * lnsmask lnss2 = xl.hstack((lnsones, lnsa1)) @ lnsW2 lnscorrect_count += np.sum(np.argmax(y, axis=1) == xl.argmax(lnss2, axis=1)) lnsaccuracy = lnscorrect_count / split print("Val-set accuracy at epoch %d: %f" % (1 + epoch, lnsaccuracy)) performance['lnsacc_val'][epoch] = 100 * lnsaccuracy print("elapsed time=" + str(time.process_time() - start_time)) fig = plt.figure(figsize=(16, 9)) ax = fig.add_subplot(111) x_axis = range(1, 1 + performance['lnsacc_train'].size) ax.plot(x_axis, performance['lnsacc_train'], 'y') ax.plot(x_axis, performance['lnsacc_val'], 'm') ax.set_xlabel('Number of Epochs') ax.set_ylabel('Accuracy') plt.suptitle(main_params['type'] + ' ' + str(split) + ' Validation and Training MNIST Accuracies F=' + str(xl.xlnsF), fontsize=14) ax.legend(['train', 'validation']) plt.grid(which='both', axis='both', linestyle='-.') plt.savefig('genericaccuracy.png') plt.show() # Now, show predictions on a few test images num_examples = 5 # Number of test images to display selected_indices = np.arange(num_examples) # choose the first few images for demo x_sample = x_test[selected_indices] y_sample = y_test[selected_indices] # For prediction, create a bias vector matching the sample size ones_sample = np.ones((x_sample.shape[0], 1)) z1_sample = np.hstack((ones_sample, x_sample)) @ lnsW1 mask_sample = (z1_sample > 0) + (leaking_coeff * (z1_sample < 0)) a1_sample = z1_sample * mask_sample z2_sample = np.hstack((ones_sample, a1_sample)) @ lnsW2 pred_probs = softmax(z2_sample) predictions = np.argmax(pred_probs, axis=1) true_labels = np.argmax(y_sample, axis=1) # Plot each test image along with its prediction and true label plt.figure(figsize=(10, 2)) for i in range(num_examples): plt.subplot(1, num_examples, i + 1) # Reshape the flattened image back to 28x28 for display plt.imshow(x_sample[i].reshape(28, 28), cmap='gray') plt.title(f"Pred: {predictions[i]}\nTrue: {true_labels[i]}") plt.axis('off') plt.tight_layout() plt.show() if __name__ == '__main__': # In a Kaggle notebook, set parameters manually using a dictionary. main_params = { 'is_training': True, 'split': 50, 'learning_rate': 0.01, 'lambda': 0.000, 'minibatch_size': 1, 'num_epoch': 5, 'leaking_coeff': 0.0078125, 'type': 'float' } main(main_params)


سأشرح لك هذا الكود الذي ينفذ نظام الأرقام اللوغاريتمية (LNS) متعدد الطبقات (MLP) لتصنيف أرقام MNIST. دعني أقسمه إلى أقسام رئيسية:


  1. الإعداد والاستيراد:
  • يستخدم الكود مكتبة xlns لعمليات نظام الأرقام اللوغاريتمية

  • إنه يوفر عدة متغيرات LNS (xlnsnp، xlnsnpv، xlnsud، إلخ) لتحقيق مقايضات مختلفة بين الدقة والأداء

  • يتم تحميل مجموعة بيانات MNIST من خلال Keras


  1. الوظائف الأساسية:
 def softmax(inp): max_vals = inp.max(axis=1) max_vals = xl.reshape(max_vals, (xl.size(max_vals), 1)) u = xl.exp(inp - max_vals) v = u.sum(axis=1) v = v.reshape((xl.size(v), 1)) u = u / v return u

هذا هو تنفيذ softmax مستقر عدديًا ومُكيف لعمليات LNS.


  1. هندسة الشبكة:
  • طبقة الإدخال: 784 خلية عصبية (28 × 28 صورة مسطحة من MNIST) + 1 تحيز = 785

  • الطبقة المخفية: 100 خلية عصبية + 1 تحيز = 101

  • طبقة الإخراج: 10 عصبونات (واحدة لكل رقم)


  1. تهيئة الوزن:
  • يتم تحميل الأوزان إما من ملف ("weightin.npz") أو يتم تهيئتها عشوائيًا

  • تستخدم الأوزان العشوائية التوزيع الطبيعي بمتوسط = 0، وانحراف معياري = 0.1

  • تتطلب متغيرات LNS المختلفة طرق تهيئة مختلفة (xlnsnp، xlnsnpv، وما إلى ذلك)


  1. حلقة التدريب:
 for epoch in range(num_epoch): for mbatch in range(int(split / batchsize)): # Forward pass lnss1 = xl.hstack((lnsones, lnsx)) @ lnsW1 lnsmask = (lnss1 > 0) + (leaking_coeff * (lnss1 < 0)) lnsa1 = lnss1 * lnsmask lnss2 = xl.hstack((lnsones, lnsa1)) @ lnsW2 lnsa2 = softmax(lnss2) # Backward pass lnsgrad_s2 = (lnsa2 - lnsy) / batchsize lnsgrad_a1 = lnsgrad_s2 @ xl.transpose(lnsW2[1:]) lnsdelta_W2 = xl.transpose(xl.hstack((lnsones, lnsa1))) * lnsgrad_s2 lnsgrad_s1 = lnsmask * lnsgrad_a1 lnsdelta_W1 = xl.transpose(xl.hstack((lnsones, lnsx))) * lnsgrad_s1


الجوانب الرئيسية للتدريب:

  • يستخدم تنشيط ReLU المتسرب (يتم التحكم فيه بواسطة leaking_coeff)

  • ينفذ الانتشار الخلفي القياسي ولكن مع عمليات LNS

  • يتضمن تنظيم L2 (معامل lambda)

  • تحديث الأوزان باستخدام الانحدار التدريجي بمعدل التعلم 'lr'


  1. تقييم:
  • يتتبع دقة التدريب والتحقق

  • يرسم منحنيات التعلم التي توضح الدقة على مر العصور

  • يعرض تنبؤات العينة على صور الاختبار


  1. المعلمات الفائقة:
 main_params = { 'is_training': True, 'split': 50, 'learning_rate': 0.01, 'lambda': 0.000, 'minibatch_size': 1, 'num_epoch': 5, 'leaking_coeff': 0.0078125, 'type': 'float' }
  • يستخدم انحدار التدرج على دفعات صغيرة (حجم الدفعة الافتراضي = 1)

  • تنفيذ التوقف المبكر من خلال تقسيم مجموعة التحقق

  • تم ضبط معامل ReLU المتسرب على 0.0078125


  1. التصور:
  • إنشاء مخططات توضح دقة التدريب والتحقق
  • يعرض صور اختبار العينة مع التوقعات والعلامات الصحيحة
  • يحفظ رسم الدقة باسم 'genericaccuracy.png'


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

مقارنة الأداء الأساسية

أداء نموذج النقطة العائمة

 Training on device: cuda Epoch [1/5], Loss: 0.8540, Train Acc: 79.60%, Val Acc: 88.22% Epoch [2/5], Loss: 0.3917, Train Acc: 88.97%, Val Acc: 89.92% Epoch [3/5], Loss: 0.3380, Train Acc: 90.29%, Val Acc: 90.60% Epoch [4/5], Loss: 0.3104, Train Acc: 90.96%, Val Acc: 91.12% Epoch [5/5], Loss: 0.2901, Train Acc: 91.60%, Val Acc: 91.62% Training completed in 57.76 seconds. 

تنبؤات نموذج MLP القائم على FP

منحنى التدريب والتحقق لنموذج MLP القائم على FP


أداء نموذج نظام الأعداد اللوغاريتمية

 At Epoch 1: train-set accuracy at epoch 1: 52.00% Val-set accuracy at epoch 1: 24.00% At Epoch 2: train-set accuracy at epoch 2: 74.00% Val-set accuracy at epoch 2: 40.00% At Epoch 3: train-set accuracy at epoch 3: 86.00% Val-set accuracy at epoch 3: 58.00% At Epoch 4: train-set accuracy at epoch 4: 94.00% Val-set accuracy at epoch 4: 70.00% At Epoch 5: train-set accuracy at epoch 5: 96.00% Val-set accuracy at epoch 5: 68.00% elapsed time = 0.35 seconds. 

تنبؤات نموذج MLP المستند إلى LNS

منحنى التدريب والتحقق لنموذج MLP المستند إلى LNS


FP مقابل LNS: مقارنات رئيسية

وجه

النقطة العائمة (FP)

نظام الأعداد اللوغاريتمية (LNS)

وقت التدريب

57.76 ثانية

0.35 ثانية

دقة القطار

91.60%

96.00%

دقة فال

91.62%

68.00%

دقة

عالي

أقل (أخطاء التقريب)

كفاءة الذاكرة

استخدام أعلى

انخفاض مساحة الذاكرة

معالجة الضرب

الضرب الأصلي

التبسيطات القائمة على الجمع

خاتمة

تمثل المقايضات بين نظام الأعداد اللوغاريتمية (LNS) والحسابات ذات النقطة العائمة (FP) دراسة حالة مثيرة للاهتمام في التصميم المشترك للأجهزة والبرامج للشبكات العصبية. في حين يوفر نظام الأعداد اللوغاريتمية مزايا كبيرة في مجالات معينة:

سرعة التدريب

  • يستبدل الضرب بالجمع في مجال اللوغاريتم
  • يقلل العمليات المعقدة إلى عمليات حسابية أبسط
  • فعالة بشكل خاص في عمليات ضرب المصفوفات في الشبكات العصبية
  • يمكن تحقيق تسريع بمقدار 2-3 مرات في بعض التطبيقات

فوائد الذاكرة

  • يتطلب عادةً عددًا أقل من البتات لتمثيل الأرقام
  • يمكن ضغط الأوزان والتنشيطات بكفاءة أكبر
  • يقلل من متطلبات عرض النطاق الترددي للذاكرة
  • استهلاك أقل للطاقة للوصول إلى الذاكرة


ومع ذلك، فإن تحديات الدقة كبيرة:

  • فقدان الدقة أثناء تراكم القيم الصغيرة
  • صعوبة تمثيل الأرقام القريبة جدًا من الصفر
  • عدم الاستقرار المحتمل في حسابات التدرج
  • قد يتطلب ضبط المعلمات الفائقة بعناية

الاتجاهات المستقبلية

هناك العديد من الأساليب الواعدة التي يمكن أن تعزز إمكانية تطبيق LNS:

1. الحسابات الخاصة بالطبقة

  • استخدم FP للطبقات الحساسة (مثل التصنيف النهائي)
  • تطبيق LNS في الطبقات المخفية التي تعتمد على الحوسبة الثقيلة
  • التبديل ديناميكيًا بناءً على المتطلبات الرقمية

2. الحوسبة المتكيفة مع الدقة

  • ابدأ التدريب مع FP لتحقيق الاستقرار
  • الانتقال تدريجيًا إلى LNS مع تقارب الأوزان
  • الحفاظ على المسارات الحرجة بدقة أعلى

3. التصميم المشترك للأجهزة

  • مسرعات مخصصة مع وحدات FP وLNS
  • الجدولة الذكية بين أنواع العمليات الحسابية
  • تسلسلات ذاكرة متخصصة لكل تنسيق

4. الابتكارات الخوارزمية

  • وظائف التنشيط الجديدة المُحسَّنة لـ LNS
  • خوارزميات التحسين المعدلة التي تحافظ على الاستقرار
  • تمثيلات الأعداد الهجينة

الدعم المحتمل لـ PyTorch

لدمج LNS في أطر التعلم العميق، يمكن استكشاف ما يلي:

1. وظائف Autograd المخصصة

  • تنفيذ عمليات LNS كوظائف autograd مخصصة
  • الحفاظ على حساب التدرج في مجال السجل
  • توفير نوى CUDA فعالة للتسريع

2. امتدادات نوع الرقم

  • إضافة أنواع موتر LNS الأصلية
  • تنفيذ العمليات الأساسية (*+, -, , / ) في مجال السجل
  • توفير أدوات التحويل من/إلى النقطة العائمة

3. تعديلات الطبقة

  • إنشاء إصدارات LNS للطبقات المشتركة (خطية، Conv2d)
  • تحسين التمريرات الخلفية لحسابات LNS
  • دعم التدريب الدقيق المختلط


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


ما هي أفكارك حول التوازن بين الدقة الرقمية والكفاءة الحسابية؟ هل واجهت حالات استخدام محددة حيث قد يكون LNS مفيدًا بشكل خاص؟


أخبرني بأفكارك حول هذا الموضوع.

مراجع


[1] G. Alsuhli، وآخرون، "أنظمة الأعداد لهندسة الشبكات العصبية العميقة: دراسة استقصائية"، arXiv:2307.05035 ، 2023.

[2] م. أرنولد، إي. تشيستر، وآخرون، "تدريب الشبكات العصبية باستخدام وحدة حسابية منطقية تقريبية بدون جداول فقط." المؤتمر الدولي الحادي والثلاثون حول الأنظمة والهندسة المعمارية والمعالجات الخاصة بالتطبيقات، معهد مهندسي الكهرباء والإلكترونيات ، 2020، ص 69-72. DOI

[3] O. Kosheleva، وآخرون، "نظام الأرقام اللوغاريتمية هو الأمثل لحسابات الذكاء الاصطناعي: التفسير النظري للنجاح التجريبي"، ورقة

[4] D. Miyashita، وآخرون، "الشبكات العصبية التلافيفية باستخدام تمثيل البيانات اللوغاريتمية"، arXiv:1603.01025 ، مارس 2016.

[5] J. Zhao et al., “LNS-Madam: Low-Precision Training in Logarithmic Number System Using Multiplicative Weight Update,” IEEE Transactions on Computers ، المجلد 71، العدد 12، ص 3179-3190، ديسمبر 2022. DOI