763 mga pagbabasa
763 mga pagbabasa

Tumatakbo ang Deep Learning sa Floating-Point Math. Paano Kung Iyan ay Isang Pagkakamali?

sa pamamagitan ng 40m2025/02/11
Read on Terminal Reader

Masyadong mahaba; Upang basahin

Ang Logarithmic Number System (LNS) ay isang alternatibong numerical na representasyon sa Floating-Point (FP) arithmetic. Ang LNS ay kumakatawan sa mga numero sa isang logarithm scale, na nagko-convert ng mga multiplikasyon sa mga karagdagan, na maaaring maging mas mura sa computation sa ilang partikular na hardware architecture. Gayunpaman, ang pagdaragdag at pagbabawas sa LNS ay nangangailangan ng mga pagtatantya, na humahantong sa pinababang katumpakan. Gumagamit kami ng LNS para sanayin ang isang simpleng ganap na konektadong Multi-Layer Perceptron sa MNIST.
featured image - Tumatakbo ang Deep Learning sa Floating-Point Math. Paano Kung Iyan ay Isang Pagkakamali?
undefined HackerNoon profile picture
0-item
1-item

Noong una kong nakita ang ideya ng paggamit ng Logarithmic Number System (LNS) sa malalim na pag-aaral, ako ay naintriga ngunit nag-aalinlangan din. Tulad ng karamihan sa atin, palagi akong nagtatrabaho sa Floating-Point (FP) arithmetic—ang pamantayan para sa numerical computation sa malalim na pag-aaral. Nagbibigay ang FP ng magandang balanse sa pagitan ng katumpakan at saklaw, ngunit may kasama itong mga trade-off: mas mataas na paggamit ng memory, tumaas na computational complex, at mas malaking konsumo ng kuryente. Kaya, nagpasya akong mag-eksperimento at tingnan para sa aking sarili—paano ang LNS kumpara sa FP kapag nagsasanay ng isang simpleng ganap na konektadong Multi-Layer Perceptron (MLP) sa MNIST?

Bakit Isaalang-alang ang LNS?

Kinakatawan ng LNS ang mga numero sa isang logarithmic scale, na nagko-convert ng mga multiplikasyon sa mga karagdagan, na maaaring maging mas mura sa computation sa ilang partikular na arkitektura ng hardware. Ang kahusayan na ito ay dumating sa halaga ng katumpakan, lalo na sa mga pagpapatakbo ng karagdagan at pagbabawas, na mas kumplikado sa LNS. Gayunpaman, ang mga potensyal na benepisyo-nabawasan ang memory footprint, mas mabilis na pag-compute, at mas mababang pagkonsumo ng kuryente-ay naging dahilan para mausisa ako upang subukan ito.

Background: Floating-Point vs. Logarithmic Number System

Representasyon ng Floating-Point (FP).

Ang Floating-Point arithmetic ay ang karaniwang numerical na representasyon sa karamihan ng mga deep learning framework, gaya ng PyTorch at TensorFlow. Ang mga numero ng FP ay mayroong:


  • Isang sign bit (pagtukoy ng positibo o negatibong halaga)
  • Isang exponent (scaling factor)
  • Isang mantissa (significand) (katumpakan ng numero)


Ang FP32 (single precision) ay karaniwang ginagamit sa malalim na pag-aaral, na nag-aalok ng balanse sa pagitan ng numerical precision at computational na kahusayan. Ang mas mahusay na mga format tulad ng FP16 at BF16 ay nakakakuha ng katanyagan upang mapabilis ang pagsasanay.

Logarithmic Number System (LNS)

Ang LNS ay isang alternatibong numerical na representasyon kung saan ang mga numero ay iniimbak bilang logarithms: [ x = \log_b (y) ] kung saan ang ( b ) ay ang logarithm base. Ang LNS ay may ilang mga pakinabang:


  • Ang multiplikasyon ay pinasimple sa karagdagan : ( x_1 * x_2 = b^{(\log_b x_1 + \log_b x_2)} )
  • Ang dibisyon ay pinasimple sa pagbabawas : ( x_1 / x_2 = b^{(\log_b x_1 - \log_b x_2)} )
  • Nagiging linear ang mga function ng exponential growth


Gayunpaman, ang pagdaragdag at pagbabawas sa LNS ay nangangailangan ng mga pagtatantya, na humahantong sa pinababang katumpakan.

LNS Arithmetic Operations

Upang higit pang galugarin ang LNS, nagpatupad ako ng mga pangunahing operasyon sa aritmetika tulad ng pagdaragdag, pagbabawas, pagpaparami, at paghahati gamit ang mga panloob na representasyon ng 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)


Narito ang isang breakdown ng aking eksperimental na pagpapatupad ng Logarithmic Number System (LNS).

1. Pangunahing Konsepto ng LNS at Mga Hamon sa PyTorch

Sa LNS, ang mga numero ay kinakatawan bilang logarithms, na binabago ang multiplikasyon at paghahati sa karagdagan at pagbabawas. Gayunpaman, ang pagpapatupad nito sa PyTorch ay nagpapakita ng mga partikular na hamon dahil ang PyTorch tensors ay gumagamit ng mga floating-point na representasyon sa loob. Lumilikha ito ng ilang mga kinakailangan:


  • Panatilihin ang logarithmic na representasyon sa buong pagkalkula.
  • Tiyakin ang katatagan ng numero.
  • Pangasiwaan nang mabuti ang mga conversion.
  • Pamahalaan ang panloob na representasyon gamit ang dalawang bahagi:
    • x : ang logarithmic na halaga.
    • s : isang sign bit (0 o 1).

2. Panloob na Representasyon at Pagbabalik-loob

Ang unang hakbang ay ang pag-convert ng mga floating-point na numero sa kanilang panloob na representasyon ng 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)


Ang paggamit ng dtype=torch.int64 ay mahalaga dahil:

  • Pinapanatili nito ang eksaktong panloob na representasyon ng LNS nang walang mga floating-point rounding error.
  • Nag-pack ng parehong logarithmic value at nag-sign bit sa isang solong integer.
  • Pinipigilan ang mga hindi sinasadyang pagpapatakbo ng floating-point na masira ang representasyon ng LNS.

3. Mga Core Arithmetic Operations

a) Pagpaparami

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

Ang pagpaparami sa LNS ay nagiging karagdagan:

  • Kung a = log(x) at b = log(y) , pagkatapos ay log(x×y) = log(x) + log(y) .

b) Dibisyon

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

Ang dibisyon ay nagiging pagbabawas:

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

c) Pagdaragdag

 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


Ang pagdaragdag ay mas kumplikado at sensitibo sa numero dahil:

  • Ito ay nagsasangkot ng exponential at logarithmic operations.
  • Ang direktang pagpapatupad ng floating-point ay maaaring humantong sa overflow/underflow.
  • Gumagamit ng equation: log(x + y) = log(max(x,y)) + log(1 + exp(log(min(x,y)) - log(max(x,y)))) .
  • Gumagamit log1p sa halip na direktang log(1 + x) para sa mas mahusay na katatagan ng numero.

4. Uri ng Pamamahala sa Kaligtasan at Conversion

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


Ang conversion pipeline ay nagpapanatili ng malinaw na paghihiwalay:

  1. I-convert mula sa float → panloob na representasyon ng LNS (mga integer).
  2. Magsagawa ng mga operasyon ng LNS gamit ang integer arithmetic.
  3. I-convert pabalik sa float lamang kung kinakailangan.
 # 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. Mga Pangunahing Kalamangan at Limitasyon

Mga kalamangan

  • Ang pagpaparami at paghahati ay pinasimple sa pagdaragdag at pagbabawas.
  • Malawak na dynamic range na may fixed-point arithmetic.
  • Posibleng mas mahusay para sa ilang partikular na application.

Mga Limitasyon

  • Ang pagdaragdag at pagbabawas ay mas kumplikadong mga operasyon.
  • Overhead ng conversion sa pagitan ng floating-point at LNS.
  • Nangangailangan ng espesyal na paghawak para sa zero at negatibong mga numero.
  • Ang PyTorch tensor compatibility ay nangangailangan ng maingat na pamamahala ng uri.

6. Mga Posibilidad sa Pag-optimize

Upang mapabuti ang pagganap, maaaring:

  1. Magpatupad ng custom na PyTorch autograd function para sa mga operasyon ng LNS.
  2. Gumawa ng custom na uri ng tensor na native na sumusuporta sa LNS.
  3. Gumamit ng mga kernel ng CUDA para sa mahusay na operasyon ng LNS sa GPU.


Ang kasalukuyang pagpapatupad ay gumagawa ng mga praktikal na trade-off:

  • Priyoridad ang kalinawan at pagpapanatili kaysa sa maximum na pagganap.
  • Gumagamit ng kasalukuyang imprastraktura ng tensor ng PyTorch habang pinapanatili ang katumpakan ng LNS.
  • Pinapanatili ang katatagan ng numero sa pamamagitan ng maingat na pamamahala ng uri.
  • Pinaliit ang mga conversion sa pagitan ng mga representasyon .

7. Halimbawa ng Daloy ng Data

Ang mga sumusunod na hakbang ay nagpapakita ng kumpletong pipeline gamit ang mga halimbawang halaga [2.0, 3.0] at [-1.0, 0.0] :

  1. I-convert ang mga float ng input sa panloob na representasyon ng LNS.
  2. Lumikha ng integer tensor upang mag-imbak ng mga halaga ng LNS.
  3. Magsagawa ng mga pagpapatakbo ng aritmetika sa domain ng LNS.
  4. I-convert ang mga resulta pabalik sa floating-point.
  5. Lumikha ng panghuling PyTorch tensor para sa karagdagang pagproseso.


Matagumpay na tinutulay ng pagpapatupad na ito ang agwat sa pagitan ng floating-point tensor system ng PyTorch at LNS arithmetic habang pinapanatili ang katatagan at katumpakan ng numero.


Pagsasanay ng ganap na konektadong MLP sa MNIST Digit dataset na may FP at LNS

Pag-setup ng Eksperimento

Nagsanay ako ng ganap na konektadong MLP sa dataset ng MNIST gamit ang parehong mga representasyon ng FP at LNS. Ang arkitektura ng modelo ay simple:

  • Input layer: 784 neurons (na-flatten na 28x28 na mga larawan)
  • Mga nakatagong layer: Dalawang layer na may 256 at 128 neuron, mga pag-activate ng ReLU
  • Output layer: 10 neuron (isa para sa bawat digit, gamit ang softmax)
  • Pagkawala ng function: Cross-entropy
  • Optimizer: Adam


Para sa pagpapatupad ng LNS, kailangan kong lumabas sa aking karaniwang daloy ng trabaho sa PyTorch. Hindi tulad ng FP, na katutubong sinusuportahan ng PyTorch, hindi nagbibigay ang PyTorch ng mga built-in na operasyon ng LNS. Nakakita ako ng proyekto sa GitHub na tinatawag na xlns , na nagpapatupad ng mga representasyon ng logarithmic number at arithmetic, na ginagawang posible na gumamit ng LNS sa mga neural network.

Floating-Point MLP sa PyTorch

Nagsisimula kami sa pamamagitan ng pagpapatupad ng karaniwang FP-based na ganap na konektadong MLP gamit ang 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)


Ang pagpapatupad na ito ay sumusunod sa isang kumbensyonal na deep learning pipeline kung saan ang mga multiplikasyon at pagdaragdag ay pinangangasiwaan ng FP arithmetic.


Narito ang isang detalyadong walkthrough ng pagpapatupad ng PyTorch na ito ng Multi-Layer Perceptron (MLP) para sa dataset ng MNIST.

  1. Arkitektura ng Modelo (MLP Class):
 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. Pasulong na Pass:
 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. Setup ng Pagsasanay:
 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] ])

Mga pangunahing bahagi:

  • Suporta ng GPU sa pamamagitan ng pagpili ng device

  • Normalization ng data para sa mas mahusay na pagsasanay

  • Mga na-configure na hyperparameter


  1. Pamamahala ng Dataset:
 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])
  • Nagda-download ng dataset ng MNIST kung wala

  • Hinahati ang data sa mga set ng pagsasanay (80%) at pagpapatunay (20%)


  1. Loop ng Pagsasanay:
 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

Klasikong pamamaraan ng pagsasanay:

  • Mga zero gradient

  • Pasulong na pass

  • Pagkalkula ng pagkawala

  • Paatras na pass

  • Mga update sa timbang


  1. Pagpapatunay:
 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()

Mga pangunahing tampok:

  • Itinakda ang modelo sa mode ng pagsusuri

  • Walang kinakailangang gradient computation

  • Pagkalkula ng katumpakan


  1. Visualization:
 def show_predictions(model, data_loader, device, num_images=6): model.eval() plt.figure(figsize=(12, 8)) # Display predictions vs actual labels
  • Nagpapakita ng mga sample na hula mula sa hanay ng pagpapatunay

  • Kapaki-pakinabang para sa pagtatasa ng husay


  1. Pagsubaybay sa Pagganap:
 # 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')
  • Sinusubaybayan ang mga katumpakan ng pagsasanay at pagpapatunay

  • Mga kurba ng pag-aaral ng mga plot

  • Sinusukat ang oras ng pagsasanay


Nagbibigay ito ng matibay na pundasyon para sa paghahambing sa mga pagpapatupad na nakabatay sa LNS, dahil ipinapatupad nito ang lahat ng karaniwang bahagi ng pipeline ng malalim na pag-aaral gamit ang tradisyonal na floating-point arithmetic.

Logarithmic Number System (LNS) MLP

Para sa LNS, kailangan nating gamitin ang xlns library. Hindi tulad ng FP, pinapalitan ng LNS ang mga multiplication-heavy operations na may karagdagan sa logarithmic domain. Gayunpaman, hindi ito native na sinusuportahan ng PyTorch, kaya kailangan nating manu-manong ilapat ang mga operasyon ng LNS kung saan naaangkop.

 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)


Ituturo ko sa iyo ang code na ito na nagpapatupad ng Logarithmic Number System (LNS) Multi-Layer Perceptron (MLP) para sa MNIST digit classification. Hayaan akong hatiin ito sa mga pangunahing seksyon:


  1. Pag-setup at Pag-import:
  • Ang code ay gumagamit ng xlns library para sa logarithmic number system operations

  • Nag-aalok ito ng maraming variant ng LNS (xlnsnp, xlnsnpv, xlnsud, atbp.) para sa iba't ibang precision at performance tradeoffs

  • Nilo-load ang dataset ng MNIST sa pamamagitan ng Keras


  1. Mga Pangunahing Pag-andar:
 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

Ito ay isang numerically stable na pagpapatupad ng softmax na inangkop para sa mga operasyon ng LNS.


  1. Arkitektura ng Network:
  • Input layer: 784 neurons (28x28 flattened MNIST images) + 1 bias = 785

  • Nakatagong layer: 100 neuron + 1 bias = 101

  • Output layer: 10 neuron (isa bawat digit)


  1. Pagsisimula ng Timbang:
  • Ang mga timbang ay maaaring ni-load mula sa isang file ("weightin.npz") o random na sinisimulan

  • Ang mga random na timbang ay gumagamit ng normal na distribusyon na may mean=0, std=0.1

  • Ang iba't ibang variant ng LNS ay nangangailangan ng iba't ibang paraan ng pagsisimula (xlnsnp, xlnsnpv, atbp.)


  1. Loop ng Pagsasanay:
 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


Mga pangunahing aspeto ng pagsasanay:

  • Gumagamit ng leaky ReLU activation (kinokontrol ng leaking_coeff)

  • Nagpapatupad ng karaniwang backpropagation ngunit may mga pagpapatakbo ng LNS

  • May kasamang L2 regularization (lambda parameter)

  • Ina-update ang mga timbang gamit ang gradient descent na may learning rate na 'lr'


  1. Pagsusuri:
  • Sinusubaybayan ang parehong katumpakan ng pagsasanay at pagpapatunay

  • Mga plot ng learning curves na nagpapakita ng katumpakan sa mga panahon

  • Nagpapakita ng mga sample na hula sa mga pansubok na larawan


  1. Mga hyperparameter:
 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' }
  • Gumagamit ng mini-batch gradient descent (default na laki ng batch = 1)

  • Nagpapatupad ng maagang paghinto sa pamamagitan ng paghahati ng set ng validation

  • Ang Leaky ReLU coefficient ay nakatakda sa 0.0078125


  1. Visualization:
  • Lumilikha ng mga plot na nagpapakita ng katumpakan ng pagsasanay at pagpapatunay
  • Nagpapakita ng mga sample na larawan ng pagsubok na may mga hula at totoong label
  • Sine-save ang katumpakan ng plot bilang 'genericaccuracy.png'


Ang pangunahing inobasyon dito ay ang paggamit ng LNS arithmetic na pinapalitan ang mga multiplikasyon ng mga pagdaragdag sa log domain, na posibleng nag-aalok ng mas mahusay na computational na kahusayan para sa ilang partikular na pagpapatupad ng hardware. Sinusuportahan ng code ang maraming variant ng LNS na nagbibigay-daan para sa iba't ibang precision-performance tradeoffs.

Pangunahing Paghahambing ng Pagganap

Pagganap ng Floating-Point na Modelo

 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. 

Mga hula ng modelong MLP batay sa FP

Kurba ng Pagsasanay at Pagpapatunay para sa Modelong MLP na batay sa FP


Pagganap ng Modelo ng Logarithmic Number System

 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. 

Mga hula ng LNS based MLP model

Training and Validation Curve para sa LNS based MLP Model


FP vs. LNS: Mga Pangunahing Paghahambing

Aspeto

Floating-Point (FP)

Logarithmic Number System (LNS)

Oras ng Pagsasanay

57.76s

0.35s

Katumpakan ng Tren

91.60%

96.00%

Katumpakan ng Val

91.62%

68.00%

Katumpakan

Mataas

Mas mababa (mga error sa pagtatantya)

Memory Efficiency

Mas mataas na paggamit

Mas mababang memory footprint

Paghawak ng Multiplikasyon

Katutubong pagpaparami

Mga pagpapasimple na nakabatay sa karagdagan

Konklusyon

Ang mga tradeoff sa pagitan ng Logarithmic Number System (LNS) at Floating-Point (FP) arithmetic ay nagpapakita ng isang kawili-wiling case study sa hardware-software co-design para sa mga neural network. Habang nag-aalok ang LNS ng mga makabuluhang pakinabang sa ilang partikular na lugar:

Bilis ng Pagsasanay

  • Pinapalitan ang multiplikasyon ng karagdagan sa log domain
  • Binabawasan ang mga kumplikadong operasyon sa mas simpleng aritmetika
  • Partikular na mahusay para sa pagpaparami ng matrix sa mga neural network
  • Maaaring makamit ang 2–3x speedup sa ilang pagpapatupad

Mga Benepisyo sa Memorya

  • Karaniwang nangangailangan ng mas kaunting mga bit upang kumatawan sa mga numero
  • Maaaring i-compress ang mga timbang at pag-activate nang mas mahusay
  • Binabawasan ang mga kinakailangan sa bandwidth ng memorya
  • Mas mababang pagkonsumo ng kuryente para sa pag-access sa memorya


Gayunpaman, ang mga hamon sa katumpakan ay makabuluhan:

  • Pagkawala ng katumpakan sa panahon ng akumulasyon ng maliliit na halaga
  • Pinagkakahirapan na kumakatawan sa mga numero na napakalapit sa zero
  • Potensyal na kawalang-tatag sa mga kalkulasyon ng gradient
  • Maaaring mangailangan ng maingat na pag-tune ng hyperparameter

Mga Direksyon sa Hinaharap

Maraming mga promising approach ang maaaring mapahusay ang applicability ng LNS:

1. Layer-Specific Arithmetic

  • Gumamit ng FP para sa mga sensitibong layer (tulad ng panghuling pag-uuri)
  • Ilapat ang LNS sa computation-heavy hidden layers
  • Dynamic na lumipat batay sa mga kinakailangan sa numero

2. Precision-Adaptive Computing

  • Simulan ang pagsasanay sa FP para sa katatagan
  • Unti-unting lumipat sa LNS habang nagtatagpo ang mga timbang
  • Panatilihin ang mga kritikal na landas sa mas mataas na katumpakan

3. Hardware Co-Design

  • Mga custom na accelerator na may parehong FP at LNS unit
  • Matalinong pag-iskedyul sa pagitan ng mga uri ng arithmetic
  • Mga espesyal na hierarchy ng memory para sa bawat format

4. Algorithmic Innovations

  • Mga bagong activation function na na-optimize para sa LNS
  • Binagong mga algorithm sa pag-optimize na nagpapanatili ng katatagan
  • Mga representasyon ng hybrid na numero

Potensyal na Suporta sa PyTorch

Upang maisama ang LNS sa mga deep learning framework, maaaring tuklasin ang sumusunod:

1. Mga Custom na Autograd Function

  • Ipatupad ang mga pagpapatakbo ng LNS bilang mga custom na autograd function
  • Panatilihin ang gradient computation sa log domain
  • Magbigay ng mahusay na mga kernel ng CUDA para sa acceleration

2. Mga Extension ng Uri ng Numero

  • Magdagdag ng mga katutubong uri ng tensor ng LNS
  • Ipatupad ang mga pangunahing operasyon (*+, -, , / ) sa log domain
  • Magbigay ng mga kagamitan sa conversion papunta/mula sa floating point

3. Mga Pagbabago sa Layer

  • Gumawa ng mga bersyon ng LNS ng mga karaniwang layer (Linear, Conv2d)
  • I-optimize ang mga backward pass para sa LNS computation
  • Suportahan ang mixed precision training


Ang komunidad ng malalim na pag-aaral ay maaaring lubos na makinabang mula sa pagsasama ng mga kakayahan na ito sa mga pangunahing balangkas, na nagbibigay-daan sa mas mahusay, mababang lakas, at mataas na bilis ng mga neural network .


Ano ang iyong mga saloobin sa balanse sa pagitan ng numerical precision at computational efficiency? Nakatagpo ka na ba ng mga partikular na kaso ng paggamit kung saan maaaring maging partikular na kapaki-pakinabang ang LNS?


Ipaalam sa akin ang iyong mga saloobin tungkol dito.

Mga sanggunian


[1] G. Alsuhli, et al., “Number System for Deep Neural Network Architectures: A Survey,” arXiv:2307.05035 , 2023.

[2] M. Arnold, E. Chester, et al., "Pagsasanay ng mga neural net gamit lamang ang tinatayang walang table na LNS ALU." 31st International Conference on Application-specific System, Architectures and Processors, IEEE , 2020, pp. 69–72. DOI

[3] O. Kosheleva, et al., "Ang Logarithmic Number System ay Pinakamainam para sa AI Computations: Theoretical Explanation of Empirical Success," Paper

[4] D. Miyashita, et al., “Convolutional Neural Networks using Logarithmic Data Representation,” arXiv:1603.01025 , Mar 2016.

[5] J. Zhao et al., “LNS-Madam: Low-Precision Training in Logarithmic Number System Using Multiplicative Weight Update,” IEEE Transactions on Computers , vol. 71, hindi. 12, pp. 3179–3190, Dis. 2022. DOI

Trending Topics

blockchaincryptocurrencyhackernoon-top-storyprogrammingsoftware-developmenttechnologystartuphackernoon-booksBitcoinbooks