paint-brush
Học sâu chạy trên toán dấu phẩy động. Nếu đó là một lỗi thì sao?từ tác giả@abhiyanampally_kob9nse8
627 lượt đọc
627 lượt đọc

Học sâu chạy trên toán dấu phẩy động. Nếu đó là một lỗi thì sao?

từ tác giả 40m2025/02/11
Read on Terminal Reader

dài quá đọc không nổi

Hệ thống số logarit (LNS) là một biểu diễn số thay thế cho số học dấu phẩy động (FP). LNS biểu diễn các số theo thang logarit, chuyển đổi phép nhân thành phép cộng, có thể rẻ hơn về mặt tính toán trong một số kiến trúc phần cứng nhất định. Tuy nhiên, phép cộng và phép trừ trong LNS yêu cầu phép xấp xỉ, dẫn đến độ chính xác giảm. Chúng tôi sử dụng LNS để đào tạo một Perceptron đa lớp hoàn toàn kết nối đơn giản trên MNIST.
featured image - Học sâu chạy trên toán dấu phẩy động. Nếu đó là một lỗi thì sao?
undefined HackerNoon profile picture
0-item
1-item

Khi lần đầu tiên tôi bắt gặp ý tưởng sử dụng Hệ thống số logarit (LNS) trong học sâu, tôi đã rất thích thú nhưng cũng hoài nghi. Giống như hầu hết chúng ta, tôi luôn làm việc với số học dấu phẩy động (FP)—tiêu chuẩn cho tính toán số trong học sâu. FP cung cấp sự cân bằng tốt giữa độ chính xác và phạm vi, nhưng nó đi kèm với sự đánh đổi: sử dụng bộ nhớ cao hơn, tăng độ phức tạp tính toán và tiêu thụ điện năng lớn hơn. Vì vậy, tôi quyết định thử nghiệm và tự mình xem—LNS so với FP như thế nào khi đào tạo một Multi-Layer Perceptron (MLP) hoàn toàn được kết nối đơn giản trên MNIST?

Tại sao nên cân nhắc LNS?

LNS biểu diễn các số theo thang logarit, chuyển đổi phép nhân thành phép cộng, có thể rẻ hơn về mặt tính toán trong một số kiến trúc phần cứng nhất định. Hiệu quả này phải trả giá bằng độ chính xác, đặc biệt là trong các phép toán cộng và trừ, vốn phức tạp hơn trong LNS. Tuy nhiên, những lợi ích tiềm năng—giảm dấu chân bộ nhớ, tính toán nhanh hơn và mức tiêu thụ điện năng thấp hơn—khiến tôi tò mò muốn thử.

Bối cảnh: Hệ thống số dấu phẩy động so với hệ thống số logarit

Biểu diễn dấu chấm động (FP)

Số học dấu phẩy động là biểu diễn số chuẩn trong hầu hết các khuôn khổ học sâu, chẳng hạn như PyTorch và TensorFlow. Số FP có:


  • Một bit dấu (xác định giá trị dương hoặc âm)
  • Một số mũ (hệ số tỷ lệ)
  • Mantissa (độ chính xác của số)


FP32 (độ chính xác đơn) thường được sử dụng trong học sâu, mang lại sự cân bằng giữa độ chính xác về số và hiệu quả tính toán. Các định dạng hiệu quả hơn như FP16 và BF16 đang ngày càng phổ biến để tăng tốc quá trình đào tạo.

Hệ thống số logarit (LNS)

LNS là một biểu diễn số thay thế trong đó các số được lưu trữ dưới dạng logarit: [ x = \log_b (y) ] trong đó ( b ) là cơ số logarit. LNS có một số ưu điểm:


  • Phép nhân được đơn giản hóa thành phép cộng : ( x_1 * x_2 = b^{(\log_b x_1 + \log_b x_2)} )
  • Phép chia được đơn giản hóa thành phép trừ : ( x_1 / x_2 = b^{(\log_b x_1 - \log_b x_2)} )
  • Các hàm tăng trưởng theo cấp số nhân trở thành tuyến tính


Tuy nhiên, phép cộng và phép trừ trong LNS đòi hỏi phải có phép tính gần đúng, dẫn đến độ chính xác giảm.

Các phép toán số học của LNS

Để khám phá sâu hơn về LNS, tôi đã triển khai các phép tính số học cơ bản như cộng, trừ, nhân và chia bằng cách sử dụng biểu diễn nội bộ của 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)


Sau đây là phân tích về quá trình triển khai thử nghiệm Hệ thống số logarit (LNS) của tôi.

1. Khái niệm cơ bản về LNS và những thách thức trong PyTorch

Trong LNS, các số được biểu diễn dưới dạng logarit, biến phép nhân và phép chia thành phép cộng và phép trừ. Tuy nhiên, việc triển khai điều này với PyTorch đặt ra những thách thức cụ thể vì tenxơ PyTorch sử dụng biểu diễn dấu phẩy động bên trong. Điều này tạo ra một số yêu cầu:


  • Duy trì biểu diễn logarit trong suốt quá trình tính toán.
  • Đảm bảo sự ổn định về số lượng.
  • Xử lý chuyển đổi một cách cẩn thận.
  • Quản lý biểu diễn nội bộ bằng hai thành phần:
    • x : giá trị logarit.
    • s : bit dấu (0 hoặc 1).

2. Biểu diễn và chuyển đổi nội bộ

Bước đầu tiên là chuyển đổi số dấu phẩy động sang dạng biểu diễn nội bộ LNS của chúng.

 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)


Việc sử dụng dtype=torch.int64 rất quan trọng vì:

  • Nó bảo toàn chính xác biểu diễn bên trong LNS mà không có lỗi làm tròn số dấu phẩy động.
  • Đóng gói cả giá trị logarit và bit dấu vào một số nguyên duy nhất.
  • Ngăn chặn các phép toán dấu phẩy động không mong muốn làm hỏng biểu diễn LNS.

3. Các phép toán số học cốt lõi

a) Phép nhân

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

Phép nhân trong LNS trở thành phép cộng:

  • Nếu a = log(x)b = log(y) thì log(x×y) = log(x) + log(y) .

b) Phân chia

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

Phép chia trở thành phép trừ:

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

c) Phép cộng

 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


Phép cộng phức tạp hơn và nhạy cảm hơn về mặt số học vì:

  • Nó bao gồm các phép toán mũ và logarit.
  • Việc triển khai trực tiếp số dấu phẩy động có thể dẫn đến tràn/tràn.
  • Sử dụng phương trình: log(x + y) = log(max(x,y)) + log(1 + exp(log(min(x,y)) - log(max(x,y)))) .
  • Sử dụng log1p thay vì log(1 + x) trực tiếp để có độ ổn định số tốt hơn.

4. Quản lý chuyển đổi và an toàn loại

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


Đường ống chuyển đổi duy trì sự tách biệt rõ ràng:

  1. Chuyển đổi từ float → biểu diễn nội bộ LNS (số nguyên).
  2. Thực hiện các phép toán LNS bằng cách sử dụng số học nguyên.
  3. Chỉ chuyển đổi lại thành float khi cần thiết.
 # 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. Ưu điểm và hạn chế chính

Thuận lợi

  • Phép nhân và phép chia được đơn giản hóa thành phép cộng và phép trừ.
  • Dải động rộng với phép tính điểm cố định.
  • Có khả năng hiệu quả hơn cho một số ứng dụng nhất định.

Hạn chế

  • Phép cộng và phép trừ là những phép toán phức tạp hơn .
  • Chi phí chuyển đổi giữa số dấu phẩy động và LNS.
  • Cần xử lý đặc biệt đối với số không và số âm.
  • Khả năng tương thích của tensor PyTorch đòi hỏi phải quản lý kiểu cẩn thận.

6. Khả năng tối ưu hóa

Để cải thiện hiệu suất, người ta có thể:

  1. Triển khai hàm tự động phân loại PyTorch tùy chỉnh cho các hoạt động LNS.
  2. Tạo một kiểu tenxơ tùy chỉnh hỗ trợ LNS.
  3. Sử dụng hạt nhân CUDA để thực hiện các hoạt động LNS hiệu quả trên GPU.


Việc triển khai hiện tại đưa ra những đánh đổi thực tế:

  • Ưu tiên sự rõ ràng và khả năng bảo trì hơn là hiệu suất tối đa.
  • Sử dụng cơ sở hạ tầng tensor hiện có của PyTorch trong khi vẫn duy trì độ chính xác của LNS.
  • Duy trì sự ổn định về số lượng thông qua việc quản lý kiểu dữ liệu cẩn thận.
  • Giảm thiểu sự chuyển đổi giữa các biểu diễn .

7. Ví dụ về luồng dữ liệu

Các bước sau đây chứng minh toàn bộ đường ống bằng cách sử dụng các giá trị ví dụ [2.0, 3.0][-1.0, 0.0] :

  1. Chuyển đổi số thực đầu vào thành biểu diễn nội bộ LNS.
  2. Tạo tenxơ nguyên để lưu trữ giá trị LNS.
  3. Thực hiện các phép tính số học trong miền LNS.
  4. Chuyển đổi kết quả trở lại dạng số dấu phẩy động.
  5. Tạo tensor PyTorch cuối cùng để xử lý tiếp theo.


Việc triển khai này đã thu hẹp thành công khoảng cách giữa hệ thống tenxơ dấu phẩy động của PyTorch và số học LNS trong khi vẫn duy trì được độ chính xác và tính ổn định về mặt số.


Đào tạo MLP được kết nối đầy đủ trên tập dữ liệu MNIST Digit với FP và LNS

Thiết lập thí nghiệm

Tôi đã đào tạo một MLP được kết nối đầy đủ trên tập dữ liệu MNIST bằng cách sử dụng cả biểu diễn FP và LNS. Kiến trúc mô hình rất đơn giản:

  • Lớp đầu vào: 784 tế bào thần kinh (hình ảnh phẳng 28x28)
  • Lớp ẩn: Hai lớp với 256 và 128 tế bào thần kinh, kích hoạt ReLU
  • Lớp đầu ra: 10 nơ-ron (một cho mỗi chữ số, sử dụng softmax)
  • Hàm mất mát: Entropy chéo
  • Người tối ưu hóa: Adam


Đối với việc triển khai LNS, tôi phải bước ra khỏi quy trình làm việc PyTorch thông thường của mình. Không giống như FP, được PyTorch hỗ trợ gốc, PyTorch không cung cấp các hoạt động LNS tích hợp. Tôi đã tìm thấy một dự án GitHub có tên là xlns , triển khai các biểu diễn số logarit và số học, giúp có thể sử dụng LNS trong mạng nơ-ron.

MLP dấu phẩy động trong PyTorch

Chúng tôi bắt đầu bằng cách triển khai MLP hoàn toàn kết nối dựa trên FP tiêu chuẩn bằng 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)


Việc triển khai này tuân theo quy trình học sâu thông thường, trong đó phép nhân và phép cộng được xử lý bằng phép tính số học FP.


Sau đây là hướng dẫn chi tiết về cách triển khai PyTorch của Multi-Layer Perceptron (MLP) cho tập dữ liệu MNIST.

  1. Kiến trúc mô hình (Lớp 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. Đường chuyền về phía trước:
 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. Thiết lập đào tạo:
 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] ])

Các thành phần chính:

  • Hỗ trợ GPU thông qua lựa chọn thiết bị

  • Chuẩn hóa dữ liệu để đào tạo tốt hơn

  • Siêu tham số có thể cấu hình


  1. Quản lý tập dữ liệu:
 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])
  • Tải xuống tập dữ liệu MNIST nếu không có

  • Chia dữ liệu thành các tập huấn luyện (80%) và xác thực (20%)


  1. Vòng lặp đào tạo:
 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

Quy trình đào tạo cổ điển:

  • Không có độ dốc

  • Đường chuyền về phía trước

  • Tính toán tổn thất

  • Đường chuyền ngược

  • Cập nhật trọng lượng


  1. Xác thực:
 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()

Các tính năng chính:

  • Mô hình được thiết lập ở chế độ đánh giá

  • Không cần tính toán độ dốc

  • Tính toán độ chính xác


  1. Hình ảnh hóa:
 def show_predictions(model, data_loader, device, num_images=6): model.eval() plt.figure(figsize=(12, 8)) # Display predictions vs actual labels
  • Hiển thị các dự đoán mẫu từ tập hợp xác thực

  • Hữu ích cho đánh giá định tính


  1. Theo dõi hiệu suất:
 # 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')
  • Theo dõi độ chính xác của đào tạo và xác thực

  • Vẽ đường cong học tập

  • Đo thời gian đào tạo


Điều này cung cấp nền tảng vững chắc để so sánh với các triển khai dựa trên LNS, vì nó triển khai tất cả các thành phần chuẩn của quy trình học sâu bằng cách sử dụng phép tính số học dấu phẩy động truyền thống.

Hệ thống số logarit (LNS) MLP

Đối với LNS, chúng ta cần sử dụng thư viện xlns . Không giống như FP, LNS thay thế các phép toán nhân nặng bằng phép cộng trong miền logarit. Tuy nhiên, PyTorch không hỗ trợ điều này, vì vậy chúng ta phải áp dụng thủ công các phép toán LNS khi có thể.

 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)


Tôi sẽ hướng dẫn bạn qua đoạn mã này, đoạn mã này triển khai Hệ thống số logarit (LNS) Multi-Layer Perceptron (MLP) để phân loại chữ số MNIST. Tôi sẽ chia nhỏ thành các phần chính:


  1. Thiết lập và Nhập khẩu:
  • Mã sử dụng thư viện xlns cho các hoạt động hệ thống số logarit

  • Nó cung cấp nhiều biến thể LNS (xlnsnp, xlnsnpv, xlnsud, v.v.) cho các sự đánh đổi về độ chính xác và hiệu suất khác nhau

  • Bộ dữ liệu MNIST được tải thông qua Keras


  1. Chức năng cốt lõi:
 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

Đây là một triển khai softmax ổn định về mặt số được điều chỉnh cho các hoạt động LNS.


  1. Kiến trúc mạng:
  • Lớp đầu vào: 784 tế bào thần kinh (hình ảnh MNIST phẳng 28x28) + 1 độ lệch = 785

  • Lớp ẩn: 100 neuron + 1 bias = 101

  • Lớp đầu ra: 10 nơ-ron (mỗi ngón tay một nơ-ron)


  1. Khởi tạo trọng số:
  • Trọng số được tải từ một tệp ("weightin.npz") hoặc được khởi tạo ngẫu nhiên

  • Trọng số ngẫu nhiên sử dụng phân phối chuẩn với trung bình = 0, độ lệch chuẩn = 0,1

  • Các biến thể LNS khác nhau yêu cầu các phương pháp khởi tạo khác nhau (xlnsnp, xlnsnpv, v.v.)


  1. Vòng lặp đào tạo:
 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


Các khía cạnh chính của khóa đào tạo:

  • Sử dụng kích hoạt ReLU bị rò rỉ (được kiểm soát bởi leaks_coeff)

  • Thực hiện truyền ngược chuẩn nhưng với các hoạt động LNS

  • Bao gồm chính quy hóa L2 (tham số lambda)

  • Cập nhật trọng số bằng cách sử dụng phương pháp giảm dần độ dốc với tốc độ học 'lr'


  1. Sự đánh giá:
  • Theo dõi cả độ chính xác của quá trình đào tạo và xác thực

  • Vẽ đường cong học tập cho thấy độ chính xác qua các thời đại

  • Hiển thị các dự đoán mẫu trên hình ảnh thử nghiệm


  1. Siêu tham số:
 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' }
  • Sử dụng phương pháp giảm dần độ dốc theo lô nhỏ (kích thước lô mặc định = 1)

  • Thực hiện dừng sớm thông qua việc phân chia tập hợp xác thực

  • Hệ số ReLU bị rò rỉ được đặt thành 0,0078125


  1. Hình ảnh hóa:
  • Tạo các biểu đồ hiển thị độ chính xác của quá trình đào tạo và xác thực
  • Hiển thị hình ảnh thử nghiệm mẫu với các dự đoán và nhãn thực
  • Lưu biểu đồ độ chính xác dưới dạng 'genericaccuracy.png'


Đổi mới chính ở đây là sử dụng phép tính số học LNS thay thế phép nhân bằng phép cộng trong miền logarit, có khả năng mang lại hiệu quả tính toán tốt hơn cho một số triển khai phần cứng nhất định. Mã hỗ trợ nhiều biến thể LNS cho phép đánh đổi hiệu suất-độ chính xác khác nhau.

So sánh hiệu suất cơ bản

Hiệu suất mô hình dấu chấm động

 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. 

Dự đoán của mô hình MLP dựa trên FP

Đường cong đào tạo và xác thực cho mô hình MLP dựa trên FP


Hiệu suất mô hình hệ thống số logarit

 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. 

Dự đoán của mô hình MLP dựa trên LNS

Đường cong đào tạo và xác thực cho mô hình MLP dựa trên LNS


FP so với LNS: So sánh chính

Diện mạo

Dấu chấm động (FP)

Hệ thống số logarit (LNS)

Thời gian đào tạo

57,76 giây

0,35 giây

Độ chính xác của tàu hỏa

91,60%

96,00%

Độ chính xác của Val

91,62%

68,00%

Độ chính xác

Cao

Thấp hơn (lỗi xấp xỉ)

Hiệu quả bộ nhớ

Sử dụng cao hơn

Dấu chân bộ nhớ thấp hơn

Xử lý phép nhân

Phép nhân bản địa

Đơn giản hóa dựa trên phép cộng

Phần kết luận

Sự đánh đổi giữa Hệ thống số logarit (LNS)số học dấu phẩy động (FP) là một nghiên cứu điển hình thú vị về thiết kế đồng thời phần cứng-phần mềm cho mạng nơ-ron. Trong khi LNS mang lại những lợi thế đáng kể trong một số lĩnh vực:

Tốc độ đào tạo

  • Thay thế phép nhân bằng phép cộng trong miền logarit
  • Giảm các hoạt động phức tạp thành số học đơn giản hơn
  • Đặc biệt hiệu quả cho phép nhân ma trận trong mạng nơ-ron
  • Có thể đạt được tốc độ tăng tốc 2–3 lần trong một số triển khai

Lợi ích của trí nhớ

  • Thông thường cần ít bit hơn để biểu diễn số
  • Có thể nén trọng lượng và kích hoạt hiệu quả hơn
  • Giảm yêu cầu về băng thông bộ nhớ
  • Tiêu thụ điện năng thấp hơn để truy cập bộ nhớ


Tuy nhiên, những thách thức về độ chính xác là rất lớn:

  • Mất độ chính xác trong quá trình tích lũy các giá trị nhỏ
  • Khó khăn khi biểu diễn các số rất gần với số không
  • Sự bất ổn tiềm ẩn trong tính toán độ dốc
  • Có thể yêu cầu điều chỉnh siêu tham số cẩn thận

Hướng đi trong tương lai

Một số phương pháp tiếp cận đầy hứa hẹn có thể nâng cao khả năng ứng dụng của LNS:

1. Số học cụ thể theo lớp

  • Sử dụng FP cho các lớp nhạy cảm (như phân loại cuối cùng)
  • Áp dụng LNS trong các lớp ẩn tính toán nặng
  • Chuyển đổi động dựa trên yêu cầu số

2. Máy tính thích ứng chính xác

  • Bắt đầu luyện tập với FP để có sự ổn định
  • Dần dần chuyển sang LNS khi trọng số hội tụ
  • Duy trì các đường dẫn quan trọng với độ chính xác cao hơn

3. Thiết kế đồng phần cứng

  • Máy gia tốc tùy chỉnh với cả đơn vị FP và LNS
  • Lên lịch thông minh giữa các loại số học
  • Phân cấp bộ nhớ chuyên biệt cho từng định dạng

4. Đổi mới thuật toán

  • Các chức năng kích hoạt mới được tối ưu hóa cho LNS
  • Các thuật toán tối ưu hóa được sửa đổi để duy trì tính ổn định
  • Biểu diễn số lai

Hỗ trợ PyTorch tiềm năng

Để tích hợp LNS vào các khuôn khổ học sâu, có thể khám phá những điều sau:

1. Chức năng Autograd tùy chỉnh

  • Triển khai các hoạt động LNS như các hàm tự động tùy chỉnh
  • Duy trì tính toán độ dốc trong miền logarit
  • Cung cấp các hạt nhân CUDA hiệu quả để tăng tốc

2. Phần mở rộng kiểu số

  • Thêm các loại tenxơ LNS gốc
  • Triển khai các hoạt động cốt lõi (*+, -, , / ) trong miền nhật ký
  • Cung cấp tiện ích chuyển đổi sang/từ dấu phẩy động

3. Sửa đổi lớp

  • Tạo phiên bản LNS của các lớp phổ biến (Tuyến tính, Conv2d)
  • Tối ưu hóa các lần truyền ngược cho tính toán LNS
  • Hỗ trợ đào tạo chính xác hỗn hợp


Cộng đồng học sâu có thể hưởng lợi rất nhiều khi tích hợp những khả năng này vào các khuôn khổ chính thống, cho phép xây dựng mạng nơ-ron hiệu quả hơn, tiết kiệm năng lượng hơn và tốc độ cao hơn .


Bạn nghĩ gì về sự cân bằng giữa độ chính xác về số và hiệu quả tính toán? Bạn đã gặp phải những trường hợp sử dụng cụ thể nào mà LNS có thể đặc biệt có lợi không?


Hãy cho tôi biết suy nghĩ của bạn về điều này.

Tài liệu tham khảo


[1] G. Alsuhli, et al., “Hệ thống số cho kiến trúc mạng nơ-ron sâu: Một khảo sát,” arXiv:2307.05035 , 2023.

[2] M. Arnold, E. Chester, et al., “Huấn luyện mạng nơ-ron chỉ sử dụng một ALU LNS không có bảng gần đúng.” Hội nghị quốc tế lần thứ 31 về Hệ thống, Kiến trúc và Bộ xử lý dành riêng cho ứng dụng, IEEE , 2020, tr. 69–72. DOI

[3] O. Kosheleva, et al., “Hệ thống số logarit là tối ưu cho các phép tính AI: Giải thích lý thuyết về thành công thực nghiệm,” Bài báo

[4] D. Miyashita, et al., “Mạng nơ-ron tích chập sử dụng biểu diễn dữ liệu logarit,” arXiv:1603.01025 , tháng 3 năm 2016.

[5] J. Zhao et al., “LNS-Madam: Huấn luyện độ chính xác thấp trong hệ thống số logarit bằng cách sử dụng bản cập nhật trọng số nhân,” IEEE Transactions on Computers , tập 71, số 12, trang 3179–3190, tháng 12 năm 2022. DOI