paint-brush
یادگیری عمیق در ریاضیات ممیز شناور اجرا می شود. اگر این یک اشتباه باشد چه؟توسط@abhiyanampally_kob9nse8
549 قرائت
549 قرائت

یادگیری عمیق در ریاضیات ممیز شناور اجرا می شود. اگر این یک اشتباه باشد چه؟

توسط 40m2025/02/11
Read on Terminal Reader

خیلی طولانی؛ خواندن

سیستم اعداد لگاریتمی (LNS) یک نمایش عددی جایگزین برای محاسبات ممیز شناور (FP) است. LNS اعداد را در مقیاس لگاریتمی نشان می‌دهد و ضرب‌ها را به جمع تبدیل می‌کند، که می‌تواند از نظر محاسباتی در معماری‌های سخت‌افزاری خاص ارزان‌تر باشد. با این حال، جمع و تفریق در LNS نیاز به تقریبی دارد که منجر به کاهش دقت می شود. ما از LNS برای آموزش یک پرسپترون چندلایه کاملا متصل ساده در MNIST استفاده می کنیم.
featured image - یادگیری عمیق در ریاضیات ممیز شناور اجرا می شود. اگر این یک اشتباه باشد چه؟
undefined HackerNoon profile picture
0-item
1-item

زمانی که برای اولین بار با ایده استفاده از سیستم اعداد لگاریتمی (LNS) در یادگیری عمیق مواجه شدم، کنجکاو شدم اما همچنین شک داشتم. مانند بسیاری از ما، من همیشه با محاسبات ممیز شناور (FP) کار کرده بودم - استانداردی برای محاسبات عددی در یادگیری عمیق. FP تعادل خوبی بین دقت و برد ایجاد می کند، اما با معاوضه هایی همراه است: استفاده بیشتر از حافظه، افزایش پیچیدگی محاسباتی و مصرف انرژی بیشتر. بنابراین، تصمیم گرفتم خودم آزمایش کنم و ببینم - چگونه LNS در هنگام آموزش یک پرسپترون چندلایه کاملاً متصل ساده در MNIST با FP مقایسه می شود؟

چرا LNS را در نظر بگیریم؟

LNS اعداد را در مقیاس لگاریتمی نشان می‌دهد و ضرب‌ها را به جمع تبدیل می‌کند، که می‌تواند از نظر محاسباتی در معماری‌های سخت‌افزاری خاص ارزان‌تر باشد. این کارایی به قیمت دقت تمام می شود، به خصوص در عملیات جمع و تفریق، که در LNS پیچیده تر هستند. با این حال، مزایای بالقوه - کاهش ردپای حافظه، محاسبات سریعتر، و مصرف انرژی کمتر - مرا به اندازه کافی کنجکاو کرد تا آن را امتحان کنم.

زمینه: سیستم اعداد ممیز شناور در مقابل سیستم اعداد لگاریتمی

نمایش نقطه شناور (FP).

محاسبات ممیز شناور نمایش عددی استاندارد در اکثر چارچوب های یادگیری عمیق، مانند PyTorch و TensorFlow است. شماره های FP دارای:


  • یک بیت علامت (تعیین مقدار مثبت یا منفی)
  • یک توان (ضریب مقیاس)
  • مانتیس (معنی دار) (دقت عدد)


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. نوع Safety and Conversion Management

 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. یک تابع خودکار PyTorch را برای عملیات LNS پیاده سازی کنید.
  2. یک نوع تانسور سفارشی ایجاد کنید که به طور بومی از LNS پشتیبانی کند.
  3. از هسته های CUDA برای عملیات LNS کارآمد در GPU استفاده کنید.


پیاده سازی فعلی معاوضه های عملی ایجاد می کند:

  • وضوح و قابلیت نگهداری را بر حداکثر عملکرد اولویت می دهد.
  • از زیرساخت تانسور موجود 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 نورون (تصاویر 28x28 مسطح)
  • لایه های پنهان: دو لایه با 256 و 128 نورون، فعال سازی ReLU
  • لایه خروجی: 10 نورون (یکی برای هر رقم، با استفاده از softmax)
  • تابع از دست دادن: آنتروپی متقابل
  • بهینه ساز: آدم


برای پیاده سازی 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 از پرسپترون چند لایه (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] ])

اجزای کلیدی:

  • پشتیبانی از GPU از طریق انتخاب دستگاه

  • نرمال سازی داده ها برای آموزش بهتر

  • هایپرپارامترهای قابل تنظیم


  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. معماری شبکه:
  • لایه ورودی: ۷۸۴ نورون (تصاویر MNIST مسطح ۲۸×۲۸) + ۱ بایاس = ۷۸۵

  • لایه پنهان: 100 نورون + 1 بایاس = 101

  • لایه خروجی: 10 نورون (یک عدد در هر رقم)


  1. مقدار اولیه وزن:
  • وزن ها یا از یک فایل بارگیری می شوند ("weightin.npz") یا به صورت تصادفی مقداردهی اولیه می شوند.

  • وزن های تصادفی از توزیع نرمال با میانگین=0 و std=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 (پارامتر لامبدا)

  • وزن ها را با استفاده از نزول گرادیان با نرخ یادگیری '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)

  • توقف اولیه را از طریق تقسیم مجموعه اعتبار سنجی اجرا می کند

  • ضریب Leaky ReLU روی 0.0078125 تنظیم شده است


  1. تجسم:
  • نمودارهایی را ایجاد می کند که دقت آموزش و اعتبار سنجی را نشان می دهد
  • تصاویر نمونه آزمایشی را با پیش بینی ها و برچسب های واقعی نمایش می دهد
  • نمودار دقت را به عنوان "genericaccuracy.png" ذخیره می کند


نوآوری کلیدی در اینجا استفاده از محاسبات LNS است که ضرب را با اضافات در دامنه log جایگزین می کند و به طور بالقوه کارایی محاسباتی بهتری را برای پیاده سازی های سخت افزاری خاص ارائه می دهد. این کد از چندین گونه 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٪

دقت Val

91.62%

68.00٪

دقت

بالا

پایین تر (خطاهای تقریبی)

کارایی حافظه

استفاده بالاتر

ردپای حافظه کمتر

مدیریت ضرب

ضرب بومی

ساده سازی های مبتنی بر جمع

نتیجه گیری

معاوضه بین سیستم اعداد لگاریتمی (LNS) و محاسبات نقطه شناور (FP) یک مطالعه موردی جالب در طراحی مشترک سخت افزار-نرم افزار برای شبکه های عصبی ارائه می دهد. در حالی که LNS مزایای قابل توجهی در زمینه های خاص ارائه می دهد:

سرعت تمرین

  • در دامنه log ضرب را با جمع جایگزین می کند
  • عملیات پیچیده را به محاسبات ساده تر کاهش می دهد
  • به ویژه برای ضرب ماتریس در شبکه های عصبی کارآمد است
  • می تواند در برخی از پیاده سازی ها به 2 تا 3 برابر سرعت برسد

مزایای حافظه

  • معمولاً برای نمایش اعداد به بیت های کمتری نیاز دارد
  • می تواند وزنه ها و فعال سازی ها را به طور موثرتر فشرده کند
  • نیاز به پهنای باند حافظه را کاهش می دهد
  • مصرف انرژی کمتر برای دسترسی به حافظه


با این حال، چالش های دقت قابل توجه هستند:

  • از دست دادن دقت در هنگام انباشت مقادیر کوچک
  • مشکل در نمایش اعداد بسیار نزدیک به صفر
  • ناپایداری بالقوه در محاسبات گرادیان
  • ممکن است نیاز به تنظیم فراپارامتر دقیق داشته باشد

مسیرهای آینده

چندین رویکرد امیدوارکننده می تواند کاربرد LNS را افزایش دهد:

1. حساب ویژه لایه

  • استفاده از FP برای لایه های حساس (مانند طبقه بندی نهایی)
  • LNS را در لایه های پنهان محاسباتی اعمال کنید
  • به صورت پویا بر اساس نیازهای عددی سوئیچ کنید

2. محاسبات تطبیقی دقیق

  • برای ثبات، تمرین را با FP شروع کنید
  • با همگرا شدن وزن ها، به تدریج به LNS منتقل شوید
  • مسیرهای بحرانی را با دقت بالاتر حفظ کنید

3. طراحی مشترک سخت افزار

  • شتاب دهنده های سفارشی با هر دو واحد FP و LNS
  • برنامه ریزی هوشمند بین انواع حسابی
  • سلسله مراتب حافظه تخصصی برای هر قالب

4. نوآوری های الگوریتمی

  • توابع فعال سازی جدید بهینه شده برای LNS
  • الگوریتم های بهینه سازی اصلاح شده که ثبات را حفظ می کند
  • نمایش اعداد ترکیبی

پشتیبانی بالقوه PyTorch

برای ادغام LNS در چارچوب های یادگیری عمیق، موارد زیر را می توان بررسی کرد:

1. توابع Autograd سفارشی

  • عملیات LNS را به عنوان توابع autograd سفارشی پیاده سازی کنید
  • محاسبات گرادیان را در دامنه log حفظ کنید
  • هسته های CUDA کارآمد را برای شتاب فراهم کنید

2. پسوندهای نوع شماره

  • انواع تانسور LNS بومی را اضافه کنید
  • عملیات اصلی (*+، -،، / ) را در دامنه گزارش اجرا کنید
  • ابزارهای تبدیل به/از ممیز شناور را ارائه دهید

3. اصلاحات لایه

  • ایجاد نسخه های LNS از لایه های رایج (خطی، Conv2d)
  • بهینه سازی گذرنامه های معکوس برای محاسبات LNS
  • پشتیبانی از آموزش دقیق ترکیبی


جامعه یادگیری عمیق می‌تواند از ادغام این قابلیت‌ها در چارچوب‌های اصلی بهره‌مند شود و شبکه‌های عصبی کارآمدتر، کم‌مصرف و با سرعت بالا را امکان‌پذیر کند.


نظر شما در مورد تعادل بین دقت عددی و کارایی محاسباتی چیست؟ آیا با موارد استفاده خاصی مواجه شده اید که LNS ممکن است به ویژه مفید باشد؟


نظرات خود را در این مورد به من بگویید.

مراجع


[1] G. Alsuhli، و همکاران، "سیستم های عددی برای معماری شبکه های عصبی عمیق: یک بررسی،" arXiv:2307.05035 ، 2023.

[2] M. Arnold، E. Chester، و همکاران، "آموزش شبکه های عصبی تنها با استفاده از یک LNS ALU تقریبی بدون جدول." سی و یکمین کنفرانس بین‌المللی سیستم‌ها، معماری‌ها و پردازنده‌های خاص برنامه، IEEE ، 2020، صفحات 69-72. DOI

[3] O. Kosheleva، و همکاران، "سیستم اعداد لگاریتمی برای محاسبات هوش مصنوعی بهینه است: توضیح نظری موفقیت تجربی"، مقاله

[4] D. Miyashita، و همکاران، "شبکه های عصبی کانولوشن با استفاده از نمایش داده های لگاریتمی،" arXiv:1603.01025 ، مارس 2016.

[5] J. Zhao و همکاران، "LNS-Madam: آموزش با دقت پایین در سیستم اعداد لگاریتمی با استفاده از به روز رسانی ضربی وزن"، IEEE Transactions on Computers ، جلد. 71، شماره 12، صفحات 3179-3190، دسامبر 2022. DOI