عندما صادفت لأول مرة فكرة استخدام نظام الأرقام اللوغاريتمية (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 : بت الإشارة (0 أو 1). s 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 يحافظ خط أنابيب التحويل على فصل واضح: التحويل من float → التمثيل الداخلي LNS (الأعداد الصحيحة). تنفيذ عمليات LNS باستخدام الحساب الصحيح. تحويل مرة أخرى إلى التعويم فقط عند الضرورة. # 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. إمكانيات التحسين لتحسين الأداء، يمكن القيام بما يلي: تنفيذ لعمليات LNS. وظيفة Autograd مخصصة لـ PyTorch إنشاء يدعم LNS بشكل أصلي. نوع موتر مخصص استخدم لعمليات LNS فعالة على وحدة معالجة الرسومات. أنوية CUDA إن التنفيذ الحالي يجعل من الممكن تحقيق مقايضات عملية: إعطاء الأولوية على الأداء الأقصى. للوضوح وإمكانية الصيانة يستخدم مع الحفاظ على دقة LNS. البنية التحتية لموتر PyTorch الحالي يحافظ على من خلال إدارة النوع الدقيقة. الاستقرار العددي يقلل . التحويلات بين التمثيلات 7. مثال على تدفق البيانات توضح الخطوات التالية خط الأنابيب الكامل باستخدام قيم المثال و : [2.0, 3.0] [-1.0, 0.0] تحويل العناصر العائمة المدخلة إلى تمثيل داخلي لـ LNS. إنشاء متجهات عددية صحيحة لتخزين قيم LNS. إجراء عمليات حسابية في مجال LNS. تحويل النتائج مرة أخرى إلى الفاصلة العائمة. إنشاء موتر 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 يسمى ، والذي ينفذ تمثيلات الأرقام اللوغاريتمية والحساب، مما يجعل من الممكن استخدام LNS في الشبكات العصبية. xlns 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. هندسة النموذج (فئة 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) تمريرة أمامية: 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 إعداد التدريب: 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] ]) المكونات الرئيسية: دعم وحدة معالجة الرسوميات من خلال اختيار الجهاز تطبيع البيانات من أجل تدريب أفضل المعلمات الفائقة القابلة للتكوين إدارة مجموعة البيانات: 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%) حلقة التدريب: 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 إجراءات التدريب الكلاسيكية: التدرجات الصفرية تمريرة للأمام حساب الخسارة تمريرة للخلف تحديثات الوزن تصديق: 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() المميزات الرئيسية: تم تعيين النموذج على وضع التقييم لا حاجة لحساب التدرج حساب الدقة التصور: def show_predictions(model, data_loader, device, num_images=6): model.eval() plt.figure(figsize=(12, 8)) # Display predictions vs actual labels يعرض تنبؤات العينة من مجموعة التحقق مفيد للتقييم النوعي تتبع الأداء: # 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، نحتاج إلى استخدام مكتبة . على عكس FP، يستبدل LNS العمليات التي تعتمد على الضرب بشكل كبير بالجمع في المجال اللوغاريتمي. ومع ذلك، لا يدعم PyTorch هذا بشكل أصلي، لذا يتعين علينا تطبيق عمليات LNS يدويًا حيثما كان ذلك مناسبًا. xlns 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. دعني أقسمه إلى أقسام رئيسية: الإعداد والاستيراد: يستخدم الكود مكتبة لعمليات نظام الأرقام اللوغاريتمية xlns إنه يوفر عدة متغيرات LNS (xlnsnp، xlnsnpv، xlnsud، إلخ) لتحقيق مقايضات مختلفة بين الدقة والأداء يتم تحميل مجموعة بيانات MNIST من خلال Keras الوظائف الأساسية: 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. هندسة الشبكة: طبقة الإدخال: 784 خلية عصبية (28 × 28 صورة مسطحة من MNIST) + 1 تحيز = 785 الطبقة المخفية: 100 خلية عصبية + 1 تحيز = 101 طبقة الإخراج: 10 عصبونات (واحدة لكل رقم) تهيئة الوزن: يتم تحميل الأوزان إما من ملف ("weightin.npz") أو يتم تهيئتها عشوائيًا تستخدم الأوزان العشوائية التوزيع الطبيعي بمتوسط = 0، وانحراف معياري = 0.1 تتطلب متغيرات LNS المختلفة طرق تهيئة مختلفة (xlnsnp، xlnsnpv، وما إلى ذلك) حلقة التدريب: 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' تقييم: يتتبع دقة التدريب والتحقق يرسم منحنيات التعلم التي توضح الدقة على مر العصور يعرض تنبؤات العينة على صور الاختبار المعلمات الفائقة: 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 التصور: إنشاء مخططات توضح دقة التدريب والتحقق يعرض صور اختبار العينة مع التوقعات والعلامات الصحيحة يحفظ رسم الدقة باسم '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. أداء نموذج نظام الأعداد اللوغاريتمية 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. 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. تعديلات الطبقة إنشاء (خطية، Conv2d) إصدارات LNS للطبقات المشتركة تحسين لحسابات LNS التمريرات الخلفية دعم التدريب الدقيق المختلط يمكن أن يستفيد مجتمع التعلم العميق بشكل كبير من دمج هذه القدرات في الأطر السائدة، مما يتيح . شبكات عصبية أكثر كفاءة وأقل استهلاكًا للطاقة وأكثر سرعة ما هي أفكارك حول التوازن بين الدقة الرقمية والكفاءة الحسابية؟ هل واجهت حالات استخدام محددة حيث قد يكون LNS مفيدًا بشكل خاص؟ أخبرني بأفكارك حول هذا الموضوع. مراجع [1] G. Alsuhli، وآخرون، "أنظمة الأعداد لهندسة الشبكات العصبية العميقة: دراسة استقصائية"، ، 2023. arXiv:2307.05035 [2] م. أرنولد، إي. تشيستر، وآخرون، "تدريب الشبكات العصبية باستخدام وحدة حسابية منطقية تقريبية بدون جداول فقط." ، 2020، ص 69-72. المؤتمر الدولي الحادي والثلاثون حول الأنظمة والهندسة المعمارية والمعالجات الخاصة بالتطبيقات، معهد مهندسي الكهرباء والإلكترونيات DOI [3] O. Kosheleva، وآخرون، "نظام الأرقام اللوغاريتمية هو الأمثل لحسابات الذكاء الاصطناعي: التفسير النظري للنجاح التجريبي"، ورقة [4] D. Miyashita، وآخرون، "الشبكات العصبية التلافيفية باستخدام تمثيل البيانات اللوغاريتمية"، ، مارس 2016. arXiv:1603.01025 [5] J. Zhao et al., “LNS-Madam: Low-Precision Training in Logarithmic Number System Using Multiplicative Weight Update,” ، المجلد 71، العدد 12، ص 3179-3190، ديسمبر 2022. IEEE Transactions on Computers DOI