尝试用float32运算器实现二个大数的加法
本文尝试用float32运算器实现二个大数的加法。先试图训练一个conv的kernel来实现,不能收敛;最后用float32的向量操作来实现
import numpy as np
import struct
import torch.nn as nn
import torch
import torch.optim as optim
def gen_rand_value(sz):
return int(''.join('{:d}'.format(np.random.randint(0,10)) for _ in range(sz)))
def to_numpy_array(value,max_len=32):
# 计算需要多少字节来表示这个整数
# 注意,这里使用的是向上取整的方式计算字节数
byte_length = (value.bit_length() + 7) // 8
# 转换成字节序列
byte_data = value.to_bytes(byte_length, 'big')
# 将字节序列转换成uint8的numpy数组
np_array = np.frombuffer(byte_data, dtype=np.uint8).astype(np.float32)
# 补齐
padded_arr = np.pad(np_array,(max_len-np_array.shape[0],0))
return padded_arr
class SimpleConvNet(nn.Module):
def __init__(self):
super(SimpleConvNet, self).__init__()
# 卷积将两个输入通道转换为一个输出通道
self.conv = nn.Conv2d(1,1, kernel_size=(2,2), stride=(1,1), padding=(0,1),bias=True)
def forward(self, x):
x = self.conv(x)
return x
# 方案一:不能收敛,不可行
def train_cnn_model_for_add():
model = SimpleConvNet()
criterion = nn.MSELoss()
optimizer = optim.Adam(model.parameters(), lr=0.01)
num_epochs=200000
for epoch in range(num_epochs):
a=gen_rand_value(64)
b=gen_rand_value(64)
c=a+b
#print("%x" % a)
#print("%x" % b)
#print("%x" % c)
a_arr = to_numpy_array(a)
b_arr = to_numpy_array(b)
c_arr = to_numpy_array(c,33)
#print(c_arr)
input_arr=np.vstack((a_arr,b_arr))
#print(input_arr.shape)
#print(c_arr.shape)
#(1,1,2,27)
inputs = torch.from_numpy(input_arr).unsqueeze(0).unsqueeze(0)
labels=torch.from_numpy(c_arr).unsqueeze(0)
#print(inputs.shape,labels.shape)
outputs = model(inputs)
#print(outputs.shape)
loss = criterion(outputs, labels)
# 反向传播和优化
optimizer.zero_grad()
loss.backward()
optimizer.step()
if epoch%1000==0:
print(f'Epoch [{epoch+1}/{num_epochs}], Loss: {loss.item():.4f}')
# 方案二: 用float32的向量操作实现
# 定义最大计算长度
max_len=32
# 定义uint8最大值
max_value=np.ones((max_len),dtype=np.float32)*256
for epoch in range(10000):
# 设置随机种子,方便复现问题
np.random.seed(epoch)
# 随机数生成长度为64的十进制大数
a=gen_rand_value(64)
b=gen_rand_value(64)
# 用python实现二个大数相加,做为Ground Truth
c_gt=a+b
# 将大数按uint8切分成向量,并转换为float类型
a_arr = to_numpy_array(a,max_len)
b_arr = to_numpy_array(b,max_len)
c_gt_arr = to_numpy_array(c_gt,max_len)
# a、b二个向量相加
sum_ab=a_arr+b_arr
# 为了去掉循环中的判断,这里假设最多出现10次进位
for i in range(10):
# 得到溢出的值(没有溢出为0)
sum_ab_overflow=sum_ab-max_value
# 哪些地方溢出了
overflow_mask=sum_ab_overflow>=0
#if overflow_mask.sum()==0: #如果是DSP,可以用判断,提前退出
# break
sum_ab_overflow=np.maximum(sum_ab_overflow, 0, sum_ab_overflow)
# 没有溢出的地方,保留它的值
sum_ab=sum_ab*(overflow_mask==0)
# 进位标记
carry_flag=np.zeros((max_len),dtype=np.float32)
carry_flag[0:max_len-1]=np.array(overflow_mask[1:max_len])
# 计算结果=溢出情况下的值 + 没有溢出时的位 + 进位的值
sum_ab=sum_ab_overflow+sum_ab+carry_flag
# 跟Ground Truth比较误差
if (sum_ab-c_gt_arr).sum()!=0:
print(f"epoch:{epoch} error")
print((sum_ab-c_gt_arr).sum())
print(sum_ab)
print(c_gt_arr)
break