Уже лет 50, со времён выхода первого издания «Языка программирования Си» Кернигана и Ритчи, известно, что «числа с плавающей запятой» одинарной точности имеют размер 32 бита, а числа двойной точности — 64 бита. Существуют ещё и 80-битные числа расширенной точности типа «long double». Эти типы данных покрывали почти все нужды обработки вещественных чисел. Но в последние несколько лет, с наступлением эпохи больших нейросетевых моделей, у разработчиков появилась потребность в типах данных, которые не «больше», а «меньше» существующих, потребность в том, чтобы как можно сильнее «сжать» типы данных, представляющие числа с плавающей запятой.

Я, честно говоря, был удивлён, когда узнал о существовании 4-битного формата для представления чисел с плавающей запятой. Да как такое вообще возможно? Лучший способ узнать об этом — самостоятельно поработать с такими числами. Сейчас мы исследуем самые популярные форматы чисел с плавающей запятой, создадим с использованием некоторых из них простую нейронную сеть и понаблюдаем за тем, как она работает.

«Стандартные» 32-битные числа с плавающей запятой

Прежде чем переходить к описанию «экстремальных» типов данных — давайте вспомним о стандартном типе. Стандарт IEEE 754, регламентирующий арифметику с плавающей запятой, был принят в 1985 году Институтом инженеров электротехники и электроники (Institute of Electrical and Electronics Engineers, IEEE). Типичное 32-битное число с плавающей запятой, в соответствии с этим стандартном, выглядит так:

None
Пример 32-битного числа с плавающей запятой (источник)

Первый бит задаёт знак числа, следующие 8 битов представляют порядок, а остальные биты — мантиссу. Десятичное значение числа находят по следующей формуле:

None
Формула для нахождения десятичного значения двоичного числа с плавающей запятой (источник)

Вот — простая вспомогательная функция, которая позволит нам выводить на экран числа с плавающей запятой в их двоичном виде:

import struct

def print_float32(val: float):
    """ Print Float32 in a binary form """
    m = struct.unpack('I', struct.pack('f', val))[0]
    return format(m, 'b').zfill(32)

print_float32(0.15625)

# > 00111110001000000000000000000000

Напишем ещё одну вспомогательную функцию, которая позволяет выполнять обратное преобразование. Позже она нам пригодится:

def ieee_754_conversion(sign, exponent_raw, mantissa, exp_len=8, mant_len=23):
    """ Convert binary data into the floating point value """
    sign_mult = -1 if sign == 1 else 1
    exponent = exponent_raw - (2 ** (exp_len - 1) - 1)
    mant_mult = 1
    for b in range(mant_len - 1, -1, -1):
        if mantissa & (2 ** b):
            mant_mult += 1 / (2 ** (mant_len - b))

    return sign_mult * (2 ** exponent) * mant_mult


ieee_754_conversion(0b0, 0b01111100, 0b01000000000000000000000)

#> 0.15625

И я надеюсь, что все программисты и IT‑энтузиасты знают, что точность чисел с плавающей запятой ограничена:

val = 3.14
print(f"{val:.20f}")

# > 3.14000000000000012434

Это, в данном случае, не такая уж и проблема. Но, чем меньше у нас бит, тем меньше точность, на которую можно рассчитывать. И, как мы скоро увидим, точность вполне может быть проблемой. А теперь — начнём путешествие по кроличьей норе…

16-битные числа с плавающей запятой

Очевидно, раньше особой потребности в 16-битных числах с плавающей запятой не было, поэтому описание соответствующего типа было добавлено в стандарт IEEE 754 только в 2008 году. У таких чисел имеется знаковый бит, 5-битный порядок и 10-битная мантисса:

None
Пример 16-битного числа с плавающей запятой (источник)

Логика преобразования десятичных представлений таких чисел в двоичные точно такая же, как и при работе с 32-битными числами, но их точность, безусловно, ниже, чем у 32-битных чисел. Выведем 16-битное число с плавающей запятой в двоичном виде:

import numpy as np

def print_float16(val: float):
    """ Print Float16 in a binary form """
    m = struct.unpack('H', struct.pack('e', np.float16(val)))[0]
    return format(m, 'b').zfill(16)

print_float16(3.14)

# > 0100001001001000

Прибегнув к методу, которым мы уже пользовались, можем выполнить обратное преобразование:

ieee_754_conversion(0, 0b10000, 0b1001001000, exp_len=5, mant_len=10)

# > 3.140625

А вот как можно найти максимальное значение, представимое в виде числа типа float16:

ieee_754_conversion(0, 0b11110, 0b1111111111, exp_len=5, mant_len=10)

#> 65504.0

Я использовал тут 0b11110 из-за того, что в стандарте IEEE 754 число 0b11111 зарезервировано для «бесконечности». Можно найти и возможное минимальное значение:

ieee_754_conversion(0, 0b00001, 0b0000000000, exp_len=5, mant_len=10)

#> 0.00006104

Для большинства разработчиков типы, вроде описанного — это «неизведанная территория». И, судя по всему, даже в наши дни в C++ нет стандартного 16-битного типа данных для чисел с плавающей запятой. Но разнообразие типов этим не ограничивается.

16-битные числа с плавающей запятой «bfloat» (BFP16)

Этот формат чисел с плавающей запятой разработан командой Google Brain. Он спроектирован специально для нужд машинного обучения (буква «B» в его названии — это сокращение от «brain»). Это — модификация «стандартного» 16-битного формата: порядок увеличен до 8 бит, в результате диапазон значений bfloat16, на самом деле, получается таким же, как у float32. Но размер мантиссы был уменьшен до 7 бит:

None
Пример 16-битного числа с плавающей запятой bfloat16 (источник)

Проведём небольшой эксперимент, аналогичный предыдущим:

ieee_754_conversion(0, 0b10000000, 0b1001001, exp_len=8, mant_len=7)

#> 3.140625

Как уже было сказано — из‑за увеличенного порядка формат bfloat16 вмещает в себя гораздо больший диапазон значений, чем float16:

ieee_754_conversion(0, 0b11111110, 0b1111111, exp_len=8, mant_len=7)

#> 3.3895313892515355e+38

Это — гораздо лучше в сравнении с 65504.0 из предыдущего примера, но, как уже было сказано, точность чисел bfloat16 ниже из‑за того, что на мантиссу приходится меньшее число бит. Можно протестировать оба типа в TensorFlow:

import tensorflow as tf

print(f"{tf.constant(1.2, dtype=tf.float16).numpy().item():.12f}")

# > 1.200195312500

print(f"{tf.constant(1.2, dtype=tf.bfloat16).numpy().item():.12f}")

# > 1.203125000000

8-битные числа с плавающей запятой (FP8)

Этот (сравнительно новый) формат был предложен в 2022 году и, как может догадаться читатель, он тоже создан для целей машинного обучения. Модели становятся всё больше и больше, их всё сложнее и сложнее умещать в памяти GPU. Формат FP8 существует в двух вариантах: E4M3 (4-битный порядок и 3-битная мантисса) и E5M2 (5-битный порядок и 2-битная мантисса):

None
Пример 8-битных чисел с плавающей запятой (источник)

Выясним максимально возможные значения чисел для обоих вариантов FP8:

ieee_754_conversion(0, 0b1111, 0b110, exp_len=4, mant_len=3)

# > 448.0

ieee_754_conversion(0, 0b11110, 0b11, exp_len=5, mant_len=2)

# > 57344.0

Формат FP8 можно использовать и в TensorFlow:

import tensorflow as tf
from tensorflow.python.framework import dtypes


a_fp8 = tf.constant(3.14, dtype=dtypes.float8_e4m3fn)
print(a_fp8)

# > 3.25

a_fp8 = tf.constant(3.14, dtype=dtypes.float8_e5m2)
print(a_fp8)

# > 3.0

Нарисуем график синуса, используя оба типа:

import numpy as np
import tensorflow as tf
from tensorflow.python.framework import dtypes
import matplotlib.pyplot as plt

length = np.pi * 4
resolution = 200
xvals = np.arange(0, length, length / resolution)
wave = np.sin(xvals)
wave_fp8_1 = tf.cast(wave, dtypes.float8_e4m3fn)
wave_fp8_2 = tf.cast(wave, dtypes.float8_e5m2)

plt.rcParams["figure.figsize"] = (14, 5)
plt.plot(xvals, wave_fp8_1.numpy())
plt.plot(xvals, wave_fp8_2.numpy())
plt.show()

Результат, что удивительно, не так уж и плох:

None
Синусоидальная волна, построенная по данным, представленным в разных вариантах формата FP8 (изображение подготовлено автором)

Тут ясно видны некоторые потери точности, но то, что получилось, очень даже похоже на синусоиду!

4-битные числа с плавающей запятой (FP4, NF4)

А теперь перейдём к самой «безумной» теме — к 4-битным числам с плавающей запятой (FP4). На самом деле такие числа — это самые компактные значения с плавающей запятой, соответствующие стандарту IEEE, имеющие 1 бит на знак, 2 бита на порядок и 1 бит на мантиссу:

None
Пример значения FP4 (изображение подготовлено автором)

Количество значений, которые можно сохранить в формате FP4, невелико. Все эти значения, на самом деле, помещаются в массив на 16 элементов!

Ещё одна возможная реализация 4-битных чисел с плавающей запятой представлена типом данных, называемым NormalFloat (NF4). Значения NF4 оптимизированы для сохранения нормально распределённых данных. Все возможные значения NF4 легко вывести на экран в виде небольшого списка (при исследовании других типов данных это может оказаться совсем непростой задачей):

[-1.0, -0.6961928009986877, -0.5250730514526367, -0.39491748809814453, 
 -0.28444138169288635, -0.18477343022823334, -0.09105003625154495, 0.0,
  0.07958029955625534, 0.16093020141124725, 0.24611230194568634, 0.33791524171829224, 
  0.44070982933044434, 0.5626170039176941, 0.7229568362236023, 1.0]

И тип FP4, и тип NF4 реализованы в Python‑библиотеке bitsandbytes. Давайте, в качестве примера, преобразуем массив [1.0, 2.0, 3.0, 4.0] в формат FP4:

from bitsandbytes import functional as bf

def print_uint(val: int, n_digits=8) -> str:
    """ Convert 42 => �' """
    return format(val, 'b').zfill(n_digits)

device = torch.device("cuda")
x = torch.tensor([1.0, 2.0, 3.0, 4.0], device=device)
x_4bit, qstate = bf.quantize_fp4(x, blocksize=64)

print(x_4bit)
# > tensor([[117], [35]], dtype=torch.uint8)

print_uint(x_4bit[0].item())
# > 01110101
print_uint(x_4bit[1].item())
# > 00100011

print(qstate)
# > (tensor([4.]), 
# >  'fp4', 
# >  tensor([ 0.0000,  0.0052,  0.6667,  1.0000,  0.3333,  0.5000,  0.1667,  0.2500,
# >           0.0000, -0.0052, -0.6667, -1.0000, -0.3333, -0.5000, -0.1667, -0.2500])])

Результат выглядит интересно. На выходе получилось два объекта: 16-битный массив [117, 35], содержащий наши 4 числа, и объект «состояния», в котором находятся коэффициент масштабирования 4.0 и тензор со всеми шестнадцатью FP4-числами.

Например, первое 4-битное число — это «0111» (=7). В объекте состояния можно видеть, что соответствующее ему значение с плавающей запятой — это 0.25; 0.25*4 = 1.0. Второе число — это «0101» (=5), а результирующее значение — 0.5*4 = 2.0. Третье число — это «0010», которое равняется 2, а соответствующее ему значение — 0.666*4 = 2.666, которое достаточно близко к 3, но не равно этому числу. Понятно, что при применении 4-битных значений мы столкнёмся с некоторой потерей точности. Последнее значение, «0011» — это 3, ему соответствует 1.000*4 = 4.0.

Понятно, что нет большой необходимости выполнять подобные вычисления вручную. С помощью bitsandbytes можно выполнить и обратное преобразование:

x = bf.dequantize_fp4(x_4bit, qstate)
print(x)

# > tensor([1.000, 2.000, 2.666, 4.000])

4-битный формат чисел тоже обладает ограниченным диапазоном значений. Например, массив [1.0, 2.0, 3.0, 64.0] будет преобразован в [0.333, 0.333, 0.333, 64.0]. Но для более или менее нормализованных данных он даёт совсем неплохие результаты. Давайте, для примера, нарисуем синусоиду, воспользовавшись данными в формате FP4:

import matplotlib.pyplot as plt
import numpy as np
from bitsandbytes import functional as bf

length = np.pi * 4
resolution = 256
xvals = np.arange(0, length, length / resolution)
wave = np.sin(xvals)

x_4bit, qstate = bf.quantize_fp4(torch.tensor(wave, dtype=torch.float32, device=device), blocksize=64)
dq = bf.dequantize_fp4(x_4bit, qstate)

plt.rcParams["figure.figsize"] = (14, 5)
plt.title('FP8 Sine Wave')
plt.plot(xvals, wave)
plt.plot(xvals, dq.cpu().numpy())
plt.show()

Тут, что неудивительно, видны некоторые потери точности, но то, что получилось, выглядит довольно прилично.

None
Синусоидальная волна, построенная по данным, представленным в формате FP4 (изображение подготовлено автором)

Если же говорить о типе NF4 — читатели сами могут попробовать исследовать его с помощью методов quantize_nf4 и dequantize_nf4; весь код останется таким же, как прежде. Но, к сожалению, на момент написания этой статьи 4-битные типы данных работают лишь с CUDA; вычисления на CPU пока не поддерживаются.

Тестирование

Теперь, в роли финального этапа этой статьи, предлагаю создать нейросетевую модель и протестировать её. При использовании Python‑библиотеки transformers можно загрузить заранее обученную модель в 4-битном формате. Для этого достаточно установить в True параметр load_in_4-bit. Но будем честны: это не приблизит нас к пониманию того, как новые форматы чисел влияют на нейросетевые модели. Вместо этого прибегнем к «игрушечному» примеру — создадим маленькую нейросеть, обучим её и воспользуемся ей, применив 4-битные числа.

Для начала создадим нейросетевую модель:

import torch
import torch.nn as nn
import torch.optim as optim
from typing import Any

class NetNormal(nn.Module):
    def __init__(self):
        super().__init__()
        self.flatten = nn.Flatten()
        self.model = nn.Sequential(
            nn.Linear(784, 128),
            nn.ReLU(),
            nn.Linear(128, 64),
            nn.ReLU(),
            nn.Linear(64, 10)
        )
      
    def forward(self, x):
        x = self.flatten(x)
        x = self.model(x)
        return F.log_softmax(x, dim=1)

Теперь надо подготовить загрузчик набора данных. Я буду использовать набор данных MNIST, содержащий 70000 изображений рукописных цифр размером 28x28 (авторские права на этот набор данных принадлежат Яну Лекуну и Коринне Кортез, он доступен по лицензии Creative Commons Attribution-Share Alike 3.0). Набор данных разделён на две части — 60000 учебных и 10000 тестовых изображений. Выбор загружаемых данных может быть выполнен в загрузчике путём использования параметра train=True|False.

from torchvision import datasets, transforms

train_loader = torch.utils.data.DataLoader(
    datasets.MNIST("data", train=True, download=True,
                   transform=transforms.Compose([
                       transforms.ToTensor(),
                       transforms.Normalize((0.1307,), (0.3081,))
                   ])),
    batch_size=batch_size, shuffle=True)

test_loader = torch.utils.data.DataLoader(
    datasets.MNIST("data", train=False, transform=transforms.Compose([
                       transforms.ToTensor(),
                       transforms.Normalize((0.1307,), (0.3081,))
                   ])),
    batch_size=batch_size, shuffle=True)

Теперь мы готовы к тому, чтобы обучить и сохранить модель. Процесс обучения выполняется «нормальным» способом, с применением стандартного формата чисел.

device = torch.device("cuda")

batch_size = 64
epochs = 4
log_interval = 500

def train(model: nn.Module, train_loader: torch.utils.data.DataLoader,
          optimizer: Any, epoch: int):
    """ Train the model """
    model.train()
    for batch_idx, (data, target) in enumerate(train_loader):
        data, target = data.to(device), target.to(device)
        optimizer.zero_grad()
        output = model(data)
        loss = F.nll_loss(output, target)
        loss.backward()
        optimizer.step()
        
        if batch_idx % log_interval == 0:
            print(f'Train Epoch: {epoch} [{batch_idx * len(data)}/{len(train_loader.dataset)}]\tLoss: {loss.item():.5f}')
            
def test(model: nn.Module, test_loader: torch.utils.data.DataLoader):
    """ Test the model """
    model.eval()
    test_loss = 0
    correct = 0
    with torch.no_grad():
        for data, target in test_loader:
            data, target = data.to(device), target.to(device)
            t_start = time.monotonic()
            output = model(data)
            test_loss += F.nll_loss(output, target, reduction='sum').item()
            pred = output.argmax(dim=1, keepdim=True)
            correct += pred.eq(target.view_as(pred)).sum().item()

    test_loss /= len(test_loader.dataset)
    t_diff = time.monotonic() - t_start

    print(f"Test set: Average loss: {test_loss:.4f}, Accuracy: {correct}/{len(test_loader.dataset)} ({100. * correct / len(test_loader.dataset)}%)\n")

def get_size_kb(model: nn.Module):
    """ Get model size in kilobytes """
    size_model = 0
    for param in model.parameters():
        if param.data.is_floating_point():
            size_model += param.numel() * torch.finfo(param.data.dtype).bits
        else:
            size_model += param.numel() * torch.iinfo(param.data.dtype).bits
    print(f"Model size: {size_model / (8*1024)} KB")

# Обучение
model = NetNormal().to(device)
optimizer = optim.SGD(model.parameters(), lr=0.01, momentum=0.5)
for epoch in range(1, epochs + 1):
    train(model, train_loader, optimizer, epoch)
    test(model, test_loader)

get_size(model)

# Сохранение
torch.save(model.state_dict(), "mnist_model.pt")

Я, кроме того, написал вспомогательный метод get_size_kb, позволяющий узнать размер модели в килобайтах.

Вот как выглядит процесс обучения модели:

Train Epoch: 1 [0/60000] Loss: 2.31558
Train Epoch: 1 [32000/60000] Loss: 0.53704
Test set: Average loss: 0.2684, Accuracy: 9225/10000 (92.25%)

Train Epoch: 2 [0/60000] Loss: 0.19791
Train Epoch: 2 [32000/60000] Loss: 0.17268
Test set: Average loss: 0.1998, Accuracy: 9401/10000 (94.01%)

Train Epoch: 3 [0/60000] Loss: 0.30570
Train Epoch: 3 [32000/60000] Loss: 0.33042
Test set: Average loss: 0.1614, Accuracy: 9530/10000 (95.3%)

Train Epoch: 4 [0/60000] Loss: 0.20046
Train Epoch: 4 [32000/60000] Loss: 0.19178
Test set: Average loss: 0.1376, Accuracy: 9601/10000 (96.01%)

Model size: 427.2890625 KB

Наша простая модель достигла точности в 96%, размер нейронной сети — 427 Кб.

А теперь — самое интересное! Создадим и протестируем 8-битную версию модели. Описание модели будет, на самом деле, таким же, как прежде. Я лишь заменил слой Linear на слой Linear8bitLt.

from bitsandbytes.nn import Linear8bitLt

class Net8Bit(nn.Module):
    def __init__(self):
        super().__init__()
        self.flatten = nn.Flatten()
        self.model = nn.Sequential(
            Linear8bitLt(784, 128, has_fp16_weights=False),
            nn.ReLU(),
            Linear8bitLt(128, 64, has_fp16_weights=False),
            nn.ReLU(),
            Linear8bitLt(64, 10, has_fp16_weights=False)
        )
      
    def forward(self, x):
        x = self.flatten(x)
        x = self.model(x)
        return F.log_softmax(x, dim=1)

device = torch.device("cuda")

# Загрузка
model = Net8Bit()
model.load_state_dict(torch.load("mnist_model.pt"))
get_size_kb(model)
print(model.model[0].weight)

# Преобразование
model = model.to(device)

get_size_kb(model)
print(model.model[0].weight)

# Запуск
test(model, test_loader)

Вот — выходные данные:

Model size: 427.2890625 KB
Parameter(Int8Params([[ 0.0071,  0.0059,  0.0146,  ...,  0.0111, -0.0041,  0.0025],
            ...,
            [-0.0131, -0.0093, -0.0016,  ..., -0.0156,  0.0042,  0.0296]]))

Model size: 107.4140625 KB
Parameter(Int8Params([[  9,   7,  19,  ...,  14,  -5,   3],
            ...,
            [-21, -15,  -3,  ..., -25,   7,  47]], device='cuda:0',
           dtype=torch.int8))

Test set: Average loss: 0.1347, Accuracy: 9600/10000 (96.0%)

Исходная модель была загружена с использованием стандартного формата чисел с плавающей запятой. Её размер остался таким же, веса выглядят как [0.0071, 0.0059,…]. Вся «магия» заключается в преобразовании модели в cuda — она становится в 4 раза меньше. Как видно, значения весов находятся в одном и том же диапазоне, поэтому преобразование модели сложностей не вызывает. В процессе проверки модели на тестовых данных оказалось, что она не потеряла ни единого процента точности!

А теперь — 4-битная версия:

from bitsandbytes.nn import LinearFP4, LinearNF4

class Net4Bit(nn.Module):
    def __init__(self):
        super().__init__()
        self.flatten = nn.Flatten()
        self.model = nn.Sequential(
            LinearFP4(784, 128),
            nn.ReLU(),
            LinearFP4(128, 64),
            nn.ReLU(),
            LinearFP4(64, 10)
        )
      
    def forward(self, x):
        x = self.flatten(x)
        x = self.model(x)
        return F.log_softmax(x, dim=1)

# Загрузка
model = Net4Bit()
model.load_state_dict(torch.load("mnist_model.pt"))
get_model_size(model)
print(model.model[2].weight)

# Преобразование
model = model.to(device)

get_model_size(model)
print(model.model[2].weight)

# Запуск
test(model, test_loader)

Вот — результаты работы:

Model size: 427.2890625 KB
Parameter(Params4bit([[ 0.0916, -0.0453,  0.0891,  ...,  0.0430, -0.1094, -0.0751],
            ...,
            [-0.0079, -0.1021, -0.0094,  ..., -0.0124,  0.0889,  0.0048]]))

Model size: 54.1015625 KB
Parameter(Params4bit([[ 95], [ 81], [109],
            ...,
            [ 34], [ 46], [ 33]], device='cuda:0', dtype=torch.uint8))

Test set: Average loss: 0.1414, Accuracy: 9579/10000 (95.79%)

Мы получили интересные результаты. После преобразования размер модели уменьшился в 8 раз — с 427 до 54 Кб, но точность упала лишь на 1%. Как это возможно? Ответить на этот вопрос несложно. По крайней мере — для этой модели:

  • Как видно, веса распределены более или менее равномерно, и потеря точности не слишком велика.

  • При обработке выходных данных в модели используется Softmax, результат определяется по индексу максимального значения. Несложно понять, что при поиске максимального индекса само значение роли не играет. Например — между 0,8 и 0,9 нет никакой разницы в том случае, если другие значения — это 0,1 или 0,2.

Полагаю — важно более тщательно изучить то, что у нас получилось. Загрузим числа из тестового набора данных и ознакомимся с тем, что выдаст модель.

dataset = datasets.MNIST('data', train=False, transform=transforms.Compose([
                       transforms.ToTensor(),
                       transforms.Normalize((0.1307,), (0.3081,))
                   ]))

np.set_printoptions(precision=3, suppress=True)  # Не использовать научную запись

data_in = dataset[4][0]
for x in range(28):
    for y in range(28):
        print(f"{data_in[0][x][y]: .1f}", end=" ")
    print()

Вот — выведенное на экран число, которое нужно распознать:

None
Вывод данных

Посмотрим, что выдаст «стандартная» модель:

# Подавить научную запись
np.set_printoptions(precision=2, suppress=True)  

# Прогноз
with torch.no_grad():
    output = model(data_in.to(device))
    print(output[0].cpu().numpy())
    ind = output.argmax(dim=1, keepdim=True)[0].cpu().item()
    print("Result:", ind)

# > [ -8.27 -13.89  -6.89 -11.13  -0.03  -8.09  -7.46  -7.6   -6.43  -3.77]
# > Result: 4

Максимальный элемент находится в 5-й позиции (элементы в массивах numpy нумеруются с 0), что соответствует числу 4.

Вот — результаты работы 8-битной модели:

# > [ -9.09 -12.66  -8.42 -12.2   -0.01  -9.25  -8.29  -7.26  -8.36  -4.45]
# > Result: 4

Вот что выдала 4-битная модель:

# > [ -8.56 -12.12  -7.52 -12.1   -0.01  -8.94  -7.84  -7.41  -7.31  -4.45]
# > Result: 4

Хорошо видно, что реальные выходные значения у разных моделей различаются, но индекс максимального элемента остаётся одним и тем же.

Итоги

В этой статье мы исследовали разные способы представления 16-битных, 8-битных и 4-битных чисел с плавающей запятой. Мы создали нейронную сеть и смогли запустить её с применением 8-битных и 4-битных чисел. И, на самом деле, за тем, как она работает, было интересно наблюдать. Уменьшая точность используемых чисел — со стандартной до 4-битной, нам удалось снизить объём памяти, необходимый модели, в 8 раз, при этом потеря точности оказалась минимальной. Конечно, мы экспериментировали на «игрушечном» примере, в по‑настоящему больших моделях используются более сложные механизмы (тем, кто интересуется данной темой, рекомендую этот материал).

Надеюсь, эта статья помогла вам получить представление об общих идеях, лежащих в основе вычислений с плавающей запятой. Как известно, «нужда — мать изобретений». Уменьшение объёма памяти, занимаемой моделью, в 4–8 раз — это замечательное достижение, особенно учитывая разницу в цене между видеокартами с памятью в 8, 16, 32 и 64 Гб ;).

Кстати, даже 4 бита — это уже не предел. В публикации о GTPQ была упомянута возможность квантификации весов в 2 или даже в три (1,5 бита!) состояния. И последнее — по порядку, но не по важности: интересно поразмышлять о «точности» нейротрансмиттеров человеческого мозга. Интуитивно понятно, что она не так уж и высока. Возможно, 2- или 4-битные нейросетевые модели ближе, чем другие, к тем «моделям», которые находятся в наших головах.

О, а приходите к нам работать? ???? ????

Мы в wunderfund.io занимаемся высокочастотной алготорговлей с 2014 года. Высокочастотная торговля — это непрерывное соревнование лучших программистов и математиков всего мира. Присоединившись к нам, вы станете частью этой увлекательной схватки.

Мы предлагаем интересные и сложные задачи по анализу данных и low latency разработке для увлеченных исследователей и программистов. Гибкий график и никакой бюрократии, решения быстро принимаются и воплощаются в жизнь.

Сейчас мы ищем плюсовиков, питонистов, дата-инженеров и мл-рисерчеров.

Присоединяйтесь к нашей команде

Комментарии (81)


  1. bolk
    27.11.2023 10:37
    +8

    Все эти форматы объединяются термином «minifloat».


  1. Goron_Dekar
    27.11.2023 10:37
    +1

    А разве не "плавающая точка"

    Кто вообще пользуется этой "запятой"? В каком компиляторе запятая поддерживается?


    1. ya_ne_znau
      27.11.2023 10:37
      +10

      В русской терминологии и математике - запятая. В английской - точка.


      1. roqin
        27.11.2023 10:37
        +6

        Запятая не только в русской, а точка не только в английской. И вообще "плавать" может не только запятая или точка.


      1. Goron_Dekar
        27.11.2023 10:37
        +4

        А в области, связанной с программированием то, что поддерживается компиляторами. Странно говорить про запятую, а писать в коде точку, не находите?

        И вообще, русская терминология устарела, и пора её менять.

        Запятая пришла к нам со времён рукописного текста - её проще ставить и заметить. Сейчас она как десятичный разделитель устарела, и надо стараться избавляться от неё всеми доступными способами, в том числе и при написании образовательных статей, чтобы новое поколение не тянуло эту legacy.


        1. Buzzzzer
          27.11.2023 10:37
          +14

          Вспомнилось, как я в школе на контрольной по математике выпендрился и везде вместо запятой поставил точку, как истинный "тру-программист", а еще ноль перечёркнутый.

          За что мне училка поставила жирную двойку, хотя решено было идеально.

          Как же у меня подгорало от вселенской несправедливости... )))


        1. SalazarMAX
          27.11.2023 10:37
          +9

          надо стараться избавляться от неё всеми доступными способами

          Желаю удачи избавиться от запятой в ЕСКД. Как получится — можно будет продолжить дискуссию о её устарелости.


          1. adeshere
            27.11.2023 10:37
            +7

            Желаю удачи избавиться от запятой в ЕСКД. Как получится — можно будет продолжить дискуссию о её устарелости.

            Тем не менее, избавляться надо. Например, в научном мире обменный формат - это чаще всего простой текстовый файл, где данные пишутся либо в выровненные столбики, либо, для сокращения объема, через запятую. Но в обоих случаях десятичный разделитель - это всегда исключительно точка:

            3.14,1917.11,42.0,...

            Так как традиционно все древние языки программирования такую запись считают стандартной и правильно ее понимают (про современные я просто не знаю). Вероятно, поэтому в русскоязычных рейтинговых журналах в статьях обычно именно точка используется (во всяком случае, в нашей области).

            Но недавно мы отправили статью в один журнал "второго эшелона", очень уважительно относящийся к Правилам русского языка. Пришлось везде по тексту статьи менять точки на запятые... после чего списки из нескольких числовых значений превращаются в непонятно что. Там надо запятые на что-то еще поменять... но на что, если запятая - это общепринятый разделитель в списке?

            Особенно интересно, когда в статье есть заимствованные картинки (сканы из цитируемых статей). Ведь там десятичный разделитель практически всегда исключительно точка. Ее-то не поменяешь (и дело даже не в технической возне с растром - это просто авторским правом запрещено).

            В общем, это боль....

            Короче, если кто-то таки пробьет это изменение Правилах русского языка (что автоматически спустится дальше в ЕСКД/ЕСПД и т.д.), ему при жизни поставят сразу несколько памятников. От IT-шников, от научных сотрудников, от учащихся школ и так далее ;-)


            1. DreamingKitten
              27.11.2023 10:37
              +6

              Какой символ является десятичным разделителем определяется локалью. Локали регулируются ISO и национальными организациями по стандартам.

              В некоторых странах десятичный разделитель это точка, в других -- запятая. Это было ещё задолго до повсеместного распространения компьютеров, используемых в задачах, где эта разница существенна. Так что и начинать вам надо оттуда.

              Там надо запятые на что-то еще поменять... но на что, если запятая - это общепринятый разделитель в списке?

              Он не общепринятый, а только в тех локалях, где задан таковым.

              И там, где десятичным разделителем является запятая, для форматирования CSV может быть использована точка с запятой. Что, кстати, почти везде стандартизовано


              1. adeshere
                27.11.2023 10:37
                +5

                Какой символ является десятичным разделителем определяется локалью. Локали регулируются ISO и национальными организациями по стандартам.

                Все так, но огромная куча научного софта игнорирует локали. При перемалывании чисел они нафиг никому не нужны, так как лишь породят жуткую кучу проблем совместимости. Я могу привести примеры, но там придется писать страницу преамбулы, чтобы подойти к сути. Поэтому приведу вместо этого аналогию. Разве не очевидно, что в идеальном мире максимально дружественного ПО компьютер должен общаться с юзером на родном языке? Вот кто-то, кому это было очевидно, когда-то давно локализовал (русифицировал) написание имен функций в Excell. Если Вы лично с последствиями не сталкивались (счастливый человек, завидую искренне!) - почитайте, сколько "благодарных" пользователей с тех пор расхлебывают этот ад. Как говорится, благими намерениями...

                Сейчас практически все научное ПО (во всяком случае в нашей области) разговаривает на своеобразном эсперанто - то есть на ASCII8 (даже как бы не 7) со стандартным использованием служебных символов. При этом многие программы применяются десятилетиями, и до сих пор являются международным стандартом, хотя их авторы уже давным-давно умерли (пример: Этерна). И они не устаревают, так как физические законы не изменяются. Но и не модифицируются...

                Еще больше существует файлов данных, сделанных в традиционных форматах. Причем старые наблюдения у нас так же важны, как и новые. Например, Вы скачиваете свежий блок наблюдений за 2022г, а затем склеиваете его с 50-ю предыдущими годовыми файлами, и запускаете обработку.

                В общем, любая попытка внедрить вместо этого эсперанто многоязычие станет попросту катастрофой для целой отрасли знаний и не вызовет ничего, кроме абсолютно ненужных проблем. Как в истории про ту бабу, которая поросенка купила...

                P.S. Я понимаю, сейчас есть модный тренд поддержки всего нетрадиционного. Но, не сочтите меня ретроградом, в тех сферах, где непрерывные наблюдения идут много лет, давайте все-таки с "нетрадиционными" форматами файлов (включая локали и разделители) будем поосторожнее? Очень-очень прошу...


                1. DreamingKitten
                  27.11.2023 10:37
                  +4

                  Все так, но огромная куча научного софта игнорирует локали.

                  Это лишь означает, что этот софт написан некорректно.

                  При перемалывании чисел они нафиг никому не нужны, так как лишь породят жуткую кучу проблем совместимости.

                  Совместимости некорректно написанного софта с корректно написанным.

                  Разве не очевидно, что в идеальном мире максимально дружественного ПО компьютер должен общаться с юзером на родном языке?

                  Очевидно. Как очевидно и то, что язык взаимодействия является частью локали.

                  Представьте себе, например, французскую локаль. В которой вторник это «Mardi», десятичный разделитель -- запятая, а разделитель групп разрядов -- пробел. В этой локали вот такое будет корректной десятичной дробью.

                  1 234 567,89

                  Почему вы считаете, что, например, locale-aware парсер даты и чисел должен понимать Mardi, но игнорировать разделители?

                  Вот кто-то, кому это было очевидно, когда-то давно локализовал (русифицировал) написание имен функций в Excell

                  Причём тут это вообще? Синтаксис языков программирования не является частью локалей.

                  Сейчас практически все научное ПО (во всяком случае в нашей области) разговаривает на своеобразном эсперанто - то есть на ASCII8

                  ...

                  В общем, любая попытка внедрить вместо этого эсперанто многоязычие

                  ASCII8 и ASCII7 это не локали, это кодировки. А то, что вы, скорее всего, имеете в виду, называется C или POSIX, и это локали. Абсолютно ничего не мешает корректно написанным программам общаться между собой в этих локалях, если уж вы так печетёсь об их интероперабельности и обратной совместимости с легаси софтом и данными.

                  давайте все-таки с "нетрадиционными" форматами файлов (включая локали и разделители) будем поосторожнее?

                  Традиционным подходом в разработке ПО является как раз-таки следование стандартам. Если с ними правильно работать, то как раз именно тех проблем, на которые вы жалуетесь, будет гораздо меньше.

                  Приведу пример, раз уж вы аргументируете Excel'ем. Он является, как ни удивительно, одним из немногих популярных продуктов, который учитывает системную локаль. В том числе -- при парсинге CSV-файлов разделителем полей в Excel считается именно тот символ, который выставлен в настройках системной локали, как на скришоте, который я привёл выше. А не тот, который кто-либо считает традиционным.

                  А вот более очевидный пример. Допустим, работая с Excel во французской локали, в ячейку типа Number я вношу десятичную дробь «1 234 567,89», которая будет корректно распарсена и сохранена в самом документе как float. При открытии этого документа в Excel в другой локали, например, американской, этот флоат будет отображён с точкой как 1234567.89, в соответствии с национальными американскими стандартами.

                  Это и есть функция локали -- обеспечивать представление данных, как вы выразились, «на родном для юзера языке», если речь идёт об интерфейсе человек-машина. На интерфейсе машина-машина пусть общаются в машинных локалях, о которых я упоминал выше.

                  Что плохого в такой схеме?


                  1. adeshere
                    27.11.2023 10:37
                    +3

                    Все так, но огромная куча научного софта игнорирует локали.

                    Это лишь означает, что этот софт написан некорректно.

                    Конкретно тот софт, который я привел в качестве примера (Этерна), был написан еще до того, как понятие "локаль" приблизилось к современному пониманию. А многоязычность программ стала стандартом. Это помимо вопроса об удобстве следования ему в конкретной среде разработки (которая в науке выбирается совсем из других соображений, чем в программировании). И тем не менее, эта программа - мировой стандарт на сегодняшний день. Поэтому позиция "Мы наш, мы новый мир построим" (вместо старого неправильного) выглядит в этом случае, хм, несколько спорной.

                    Хотя... Если Вы настаиваете, что действительно нужные программы (а Этерна в их число совершенно точно входит) следует переписать по стандартам, и что ради этого не грех приложит достаточные усилия - то флаг в руки. Правда, конкретно в этом случае нам для этого придется как-то вытащить ее автора обратно в наш мир... Но если (чем черт не шутит?) Вы и правда сумеете это сделать, то вне зависимости от того, уговорится ли он переделать свое ПО, или нет, множество народу будет Вам благодарно. Включая и те десятки людей, которые пытались переписать Этерну в более современном виде уже после ухода автора... но вот не сумели.

                    И второй нюанс.

                    Научный софт не пишется коллективами программистов, у которых есть куча ресурсов, в том числе и на организацию "правильного" интерфейса. Чаще всего такие программы - это результат труда одно-двух гениев, главные знания и умения которых - это вовсе не программирование. Они сначала делают что-то такое, что никто в мире не смог, а потом в качестве "побочного эффекта" реализуют это в виде программы. Один такой выдающийся человек получил достаточно серьезные результаты в теории земных приливов, и реализовал эти свои достижения в виде программы Атлантида, которая эти приливы рассчитывает. А для организации интерфейса к своим алгоритмам он взял какую-то библиотеку, которая как раз-таки умеет в локали. Я даже не знаю, какую именно... но что-то он там недокрутил немного (или может в библиотеке была ошибка), и теперь все пользователи Атлантиды при

                    вводе параметров счета

                    испытывают чудовищные мучения, так как при чтении введенных параметров настройки локали обрабатываются одним методом, а при вставке умолчаний - каким-то другим, и эти методы конфликтуют. В результате чего во всех полях ввода приходится вручную править точки на запятые или наоборот. И на большинстве компов никакие настройки ОС не помогают это исправить. То есть лично у автора на компе по какому-то стечению обстоятельств все работало... а больше почти ни у кого не работает.

                    А тестировать свою программу на других версиях ОС у таких людей обычно просто возможности нет. Так как такая отладка требует еще плюс 50% к времени написания работающего кода. Плюс надо где-то эти компы физически взять, и т.д. и т.п. А у них совсем другие задачи в приоритете.

                    Ну а когда стало понятно, что глюк этот массовый и весьма неприятный, то у автора что-либо править уже физической возможности не было :-(

                    Да, современные продвинутые программисты, которые сами никогда не совершают ошибок, с высоты прожитых лет наверно имеют моральное право посмеиваться над дремучим неучем из научной среды, который не понимает разницу между "корректно" и "некорректно" написанным софтом. Только вот если бы этот неуч посвятил свое время изучению программирования, то никакого вклада в развитие теории приливов он бы, скорее всего, не внес. Попросту не успел бы. И самой этой программы не было бы.

                    Это я все к чему говорю: если бы автор Атлантиды не заморачивался с попыткой учета "стандартов", а тупо использовал в качестве десятичного разделителя точку всегда и везде, то ВСЕМ было бы гораздо удобнее. Включая и тех же французов. Которые, кстати, его Атлантиду используют.


                    1. DreamingKitten
                      27.11.2023 10:37
                      +1

                      Конкретно тот софт, который я привел в качестве примера (Этерна), был написан еще до того, как понятие "локаль" приблизилось к современному пониманию.

                      Позвольте усомниться.

                      Локали в более-менее современном виде появились в POSIX, а это начало 1980-х. Но я понимаю, о чём вы. Ни Фортран, ни Бейсик не умеют нативно в локали и до сих пор.

                      Поэтому позиция "Мы наш, мы новый мир построим" (вместо старого неправильного) выглядит в этом случае, хм, несколько спорной.

                      Новый мир уже давно построен и работает. Это как раз вы пытаетесь отстоять и оправдать исключительность очень узко-нишевого легаси, написанного на косячных инструментах без намёка на удобство сопровождения продукта.

                      Но если (чем черт не шутит?) Вы и правда сумеете это сделать,

                      ...

                      множество народу будет Вам благодарно

                      За ваши деньги -- любой каприз.

                      Научный софт не

                      Вот это вы сейчас лихо рассказываете, какие программы пишут люди из научной среды как-раз таки человеку из научной среды, который в этой среде пишет программы. Не надо частные особенности возводить в правило и, тем более, в культ.

                      если бы автор Атлантиды не заморачивался с попыткой учета "стандартов", а тупо использовал в качестве десятичного разделителя точку всегда и везде,

                      Аргументировать несоблюдение стандартов криворукостью автора это мощно, да.

                      И я понимаю, что таких может быть даже большинство. Но это не делает данный подход верным, а всего лишь распространённым.


                      1. adeshere
                        27.11.2023 10:37
                        +1

                        Локали в более-менее современном виде появились в POSIX, а это начало 1980-х.

                        Именно Этерна писалась и работала в DOS. Подавляющее число обычных людей тогда о локалях не задумывалось, насколько я помню. Если бы автор какой-то консольной программы типа Этерны сделал настраиваемый десятичный разделитель, на него бы посмотрели, как на идиота.

                        Кроме того, сам факт зарождения современного стандарта в стародавние времена нам виден только сейчас. А в те стародавние времена разных стандартов в информатике было множество (например, экран 25х80), и какой из них выживет (а какой умрет/эволюционирует), обычному человеку было совершенно не ясно.

                        Аргументировать несоблюдение стандартов криворукостью автора (...) не делает данный подход верным, а всего лишь распространённым.

                        Я наверно плохо сформулировал свою мысль, и из-за этого остался непонятым. Я имел в виду, что профессионализм в любой области не дается бесплатно. А высокий профессионализм (когда ты один из лучших в мире, а то и вовсе лучший) - стоит очень большого времени. Тот человек, о котором я написал, в своей области был лучшим. При этом он еще и сформулировал свои алгоритмы в виде общедоступной программы, которой пользуются специалисты из разных стран мира, так как она лучше других аналогов. Тестирование на данных европейских станций сети сверхпроводящих гравиметров показало, что Атлантида на треть точнее, чем самые продвинутые альтернативы.

                        Так вот, говоря чисто теоретически, он мог бы использовать более простой интерфейс (без претензии на учет локалей), и сделал бы более удобную в использовании программу с тем же самым функционалом. Я тут защищаю именно этот подход. Даже несмотря на всю его ущербность, которую мне тут уже достаточно доходчиво объяснили.

                        Но позвольте тогда и мне объяснить, почему я защищаю столь странную в современном мире позицию. Дело в том, что если бы этот конкретный Е.А. больше времени посвятил программированию (вместо развития теории приливов), то он, несомненно, научился бы писать программы без глюков. Только вот в этом случае у него не осталось бы времени получить прорывные результаты в теории приливов. И его программа с локалями и без глюков была бы никому не нужна.

                        В сети есть хороший мем

                        качественно-дешево-быстро

                        В нашем случае ситуация полностью аналогичная. Все, что Вы пишете - правильно. Проблема лишь в том, что в рассмотренных мной конкретных условиях (научный институт, причем вовсе не обязательно здесь в РФ-ии) этого "не бывает".


                  1. Tanriol
                    27.11.2023 10:37
                    +3

                    А excel уже научился в русской локали автоматически детектировать и правильно считывать csv-шки с десятичной точкой? А то очень неудобно, что при использовании csv как формата обмена между системами с разными локалями нет универсального варианта - или в одной системе нужно вручную выставлять правильный разделитель, или в другой.

                    Это не только проблемы "совместимости некорректно написанного софта с корректно написанным", но и проблемы совместимости форматов. В том числе передачи данных.

                    Синтаксис языков программирования не является частью локалей.

                    Что интересно, я вроде бы не слышал, чтобы какой-нибудь язык программирования использовал локализованные разделители при вводе чисел, или, например, списков. По-моему, большинство в последнее время сходится на общем формате с точкой в роли десятичного разделителя и подчёркиванием в роли разделителя групп разрядов.


                    1. DreamingKitten
                      27.11.2023 10:37

                      А excel уже научился в русской локали автоматически детектировать и правильно считывать csv-шки с десятичной точкой?

                      А должен? В русской локали, ЕМНИП, десятичный разделитель это запятая.

                      или в одной системе нужно вручную выставлять правильный разделитель, или в другой.

                      А лучше вообще не использовать человеко-читаемые форматы данных на интерфейсе машина-машина.

                      Что интересно, я вроде бы не слышал, чтобы какой-нибудь язык программирования использовал локализованные разделители при вводе чисел, или, например, списков.

                      Совершенно верно. Именно по той причине, которую я указал выше -- синтаксис языка не является ни частью локали, ни даже национальным стандартом.

                      Но вот данные, которые считывают программы, написанные на этом языке, национально-специфичному форматированию вполне могут быть подвержены. Нет здесь противоречий.


                      1. Tanriol
                        27.11.2023 10:37
                        +2

                        Формально, наверное, не должен... если мы предполагаем, что обмен csv-файлами между пользователями с разными локалями событие редкое. В моём случае, увы, это не так.
                        Насчёт "не использовать человекочитаемые форматы на интерфейсе машина-машина" - не могу полностью согласиться. Как минимум это зачастую может существенно упростить отладку. Не говоря уже о том, что многие интерфейсы не чисто человек-машина, а смешанные, пригодные для использования как машинами, так и людьми.
                        И да, для меня лично единообразие между представлением чисел в программном коде и во всех остальных случаях (а также функционирование ПО, которое не полностью корректно отрабатывает десятичные разделители) достаточно приоритетно, чтобы всегда в своих компьютерах использовать английскую локаль.


                      1. DreamingKitten
                        27.11.2023 10:37

                        Ну, в вашем случае, возможно. Но, честно говоря, для меня и сам по себе CSV это какое-то извращение. Есть же protobuf, есть bson, да куча всего есть. Да даже json, если вам человеко-читаемость так сильно упёрлась -- там точка закреплена стандартом.


                      1. johnfound
                        27.11.2023 10:37
                        +2

                        А лучше вообще не использовать человеко-читаемые форматы данных на интерфейсе машина-машина.

                        Ну да, ну да! И одновременно закрыть все Линуксы/Юниксы, где это считается лучшей практикой. Пусть все платят умным парням от MS. Они вам все локали исправят и сделают WYSIWYG.


                      1. DreamingKitten
                        27.11.2023 10:37

                        А покажите, пожалуйста, где именно в Линуксе это считается лучшей практикой.



                      1. DreamingKitten
                        27.11.2023 10:37
                        -2

                        Нет там такого.


                      1. johnfound
                        27.11.2023 10:37
                        +1

                        пишите программы, которые бы поддерживали текстовые потоки, поскольку это универсальный интерфейс.
                        Write programs to handle text streams, because that is a universal interface.

                        храните данные в простых текстовых файлах;
                        Store data in flat text files.


                  1. adeshere
                    27.11.2023 10:37
                    +1

                    ASCII8 и ASCII7 это не локали, это кодировки. А то, что вы, скорее всего, имеете в виду, называется C или POSIX, и это локали. Абсолютно ничего не мешает корректно написанным программам общаться между собой в этих локалях, если уж вы так печетёсь об их интероперабельности и обратной совместимости с легаси софтом и данными.

                    Я понимаю разницу между кодировками и локалями. Но научный софт - это вовсе не черный ящик, где надо нажать кнопку "сделай мне хорошо". В эти файлы почти всегда приходится сперва "лазить руками". И ситуация, когда в файле десятичным разделителем будет точка, а в интерфейсе программы - запятая, будет катастрофически неудобна для того, кто с этим работает. Так как у него основная часть головы занята совсем другим, а не форматами/кодировками. А если в одном файле будет запятая, а в другом точно таком же точка (имеющая тот же смысл), то это

                    вообще катастрофа

                    Если, конечно, не разделить науку на страны по принципу "точка, точка, запятая" и не запретить ученым из разных зон общаться между собой (прощай, глобальная сеть метеонаблюдений, а также сейсмическая, гравиметрическая и т.д.).

                    так как все эти файлы сплошь и рядом режутся на куски и склеиваются в разных пропорциях в ходе работы. Или Вы предлагаете в каждый файл добавить пометку, в какой локали он создан? Идея, наверное, неплохая, если, конечно, одновременно с принятием такого решения сразу же провести рефакторинг абсолютно всех существующих файлов данных (сколько их там в мире всего?) и абсолютно всего софта. Да, забыл: надо же ведь еще как-то заставить абсолютно весь мир с этим рефакторингом согласиться...

                    В общем, в каком-то сферическом вакууме какие-то идеально написанные программы может и смогут со всем этим работать... но я в соседних ветках привел кучу примеров, что в реальной жизни это в принципе невозможно.

                    Поэтому конкретно в науке нужна простая и тупая унифицированная система, к которой один раз привыкаешь - и все, навсегда.

                    И в науке такая система уже есть де-факто. Она включает огромное количество файлов данных и огромное число программ, которые в этом стандарте работают. Прекрасно выполняя свои задачи. ПРичем, эти программы используют точку в качестве десятичного разделителя даже в тех странах, где обычно разделителем является запятая. То есть, имеется один универсальный язык, на котором де-факто разговаривают абсолютно все. И всех понимают.

                    Вы предлагаете всю эту систему сломать, заменить чем-то более совершенным, но ради чего? Ради какого-то абстрактного идеала? Но пользователи таких программ - это не бабушки на кухне, которые привыкли запятой в ценнике, и точка в неправильном месте сразу заканчивается инфарктом. Любые улучшения должны идти на пользу тем, кто этим софтом пользуется. А не ради стандарта (который к тому же еще и меняется иногда).

                    Мир таков, каков он есть, и это надо принять и смириться. А все улучшения пусть внедряются исключительно добровольно. В противном случае можно закончить насильственным внедрением системы СИ, например, в США - ради общего блага, естественно. А если вдруг кто-нибудь будет против, то это неправильный человек и ему не место в цивилизованном мире. Можно его в резервацию, а лучше сразу атомной бомбой.... Опыт показывает, что такие попытки насильно сделать людей счастливыми ничем хорошим никогда не заканчиваются. Увы.


                    1. DreamingKitten
                      27.11.2023 10:37

                      И в науке такая система уже есть де-факто.

                      Не спешите расписываться за всю науку. Я довольно много работал с сырыми данными миссий SRTM да и вообще с DEM моделями, как для пет-проектов, так и для муниципальных. И не было там никаких точек и десятичных разделителей вообще. Чистая бинарщина, двухкоординатный меш из 16-bit signed big-endian. Что есть архитектурно правильно.

                      Вы предлагаете всю эту систему сломать

                      Нет. Я предлагаю хотя бы называть вещи своими именами. Кривое легаси -- кривым легаси.

                      В противном случае можно закончить насильственным внедрением системы СИ, например, в США - ради общего блага, естественно.

                      А вы в курсе, что в этих самых США однажды всю космическую индустрию вместе с NASA взяли да и принудительно перевели как раз таки на систему СИ?

                      Потому что до того кто-то там посчитал параметры Mars Climate Orbiter в одной неметрической системе, а кто-то другой -- в другой, но тоже неметрической и в результате корабль был потерян с концами. Это к вопросу о том, нужно ли стандарты соблюдать или делать «как привычно».


                      1. adeshere
                        27.11.2023 10:37

                        Не спешите расписываться за всю науку.

                        Возражение принимается. Разумеется, я знаком лишь с несколькими ее разделами. Но во всех этих разделах (довольно разных) ЕСЛИ обменный формат текстовый, ТО разделитель всегда точка. Отсюда я экстраполировал, что это почти всегда так. Ваш пример, между прочим, этот мой тезис не опровергает. Кстати, не для спора, но просто интересно: если кто-то видел текстовый обменный формат с десятичным разделителем-запятой, поделитесь, пожалуйста?

                        Кривое легаси -- кривым легаси.

                        Легаси - согласен, кривое - нет.

                        Так как эта система прекрасно работает и полностью самосогласованна, пока ее не пытаются насильственно адаптировать к тем стандартам, для которых все это не предназначено. Если обмен данными идет успешно, расчеты выполняются правильно, а все участники цепочки довольны, т.е. целевая функция достигается, то я нахожу странным называть такую систему "кривой".

                        А вы в курсе, что в этих самых США однажды всю космическую индустрию вместе с NASA взяли да и принудительно перевели как раз таки на систему СИ?

                        Нет, детали не знал. Но здесь речь идет об инженерном проекте, а на заправке, я надеюсь, никто этих людей не заставляет покупать бензин в кубометрах?

                        В фундаментальной науке очень многие программы пишутся в расчете на одного юзера и на использование "один раз" (т.е. в рамках единственного проекта). И в момент написания (пока результаты не получены и не проанализированы) далеко не всегда можно предугадать, что программа станет нужна многим людям и постоянно. Поэтому изначально закладывать туда что-то сверх минимально необходимого никто не будет (да и не сможет физически). Но даже когда востребованность наметилась (а это может случиться через год после завершения проекта, когда статью прочтут и заинтересуются), серьезно вкладываться в доработку интерфейса мало кто станет, так как автор программы обычно уже увлечен другими идеями и задачами. Ему новый проект двигать надо. Хорошо, если он к консольному приложению прикрутит GUI... а чаще просто - берите as is. Это же не коммерция, автор за доработку программы (почти) никаких бонусов не получит.

                        Это к вопросу о том, нужно ли стандарты соблюдать или делать «как привычно».

                        Ну кто же спорит, что лучше быть богатым и здоровым одновременно. "Как привычно" - это же не от вредности. Конечно хорошо, когда ученый умеет и то, и другое на высшем уровне. Только вот "не бывает"...


                      1. VADemon
                        27.11.2023 10:37
                        -1

                        А вы в курсе, что в этих самых США однажды всю космическую индустрию вместе с NASA взяли да и принудительно перевели как раз таки на систему СИ?

                        Имперская система для кого-то такой же стандарт, как написание чисел. Проблема возникла на стыке стандартов. Аж чем-то локали напоминает. Человек и предлагает свести хотя бы научные данные к единому стандарту написания. Прям как NASA.


              1. Goron_Dekar
                27.11.2023 10:37
                +3

                Ещё раз, вы путаете возможность и должествование. Возможно использовать запятую? Возможно, даже для этого есть стандарты, локали и традиции.

                ДОлжно ли использовать запятую, и обрекать людей на проблемы, дополнительные ошибки, и, как следствие, более высокую стоимость разработки? Конечно, нет!

                Я всегда на виндовых компах, где работаю, правлю настройки локали чтобы запятой не было, потому что иначе имею проблемы с частью научного и рассчётного софта. Поэтому в моих лабах настройки локали - точка.


                1. DreamingKitten
                  27.11.2023 10:37

                  Это не я путаю.

                  ДОлжно ли использовать запятую

                  Должно правильно работать с локалями. Т.е. перекладывать заботу о том, точка там нужна или запятая на подсистему ОС или рантайм, а не на юзера и тем более не на хардкод. Экономия на standard compliance всегда вылазит боком, в том числе и по деньгам, просто немного позже в жизненном цикле продукта.

                  А если вы правильным подходом считаете править системные настройки в угоду криво написанному софту, то рано или поздно у вас возникнет ситуация, когда две разные софтины потребуют взаимоисключающих правок. И вам придётся в размышлениях о высокой стоимости разработки лепить какое-то middleware.


                  1. Goron_Dekar
                    27.11.2023 10:37
                    +1

                    Компиляторы должны работать с локалями?


                    1. DreamingKitten
                      27.11.2023 10:37
                      +1

                      Если компиляторы занимаются чтением и обработкой данных, которые подвержены национально-специфичному форматированию -- то да, иначе -- нет.


                      1. Goron_Dekar
                        27.11.2023 10:37
                        +2

                        Назовите мне признаки "данных, которые подвержены национально-специфичному форматированию".

                        Код это такие данные?


                      1. DreamingKitten
                        27.11.2023 10:37
                        +1

                        Признак: данные, которые подвержены национально-специфичному форматированию.

                        Например, локализованные названия дней недели, месяцев, метки am/pm, да даже банальный DD-MM-YYYY или MM-DD-YYYY.

                        Код это такие данные?

                        Нет, не такие. А к чему вопрос?


                      1. Goron_Dekar
                        27.11.2023 10:37

                        Признак: данные, которые подвержены национально-специфичному форматированию.

                        Так признакт-то какие? Как, глядя на данные, мне нужно понять, что их надо представлять в машино (компиляторо) нечитаемом виде? Мне кажется, что это только те данные, с которыми больше не будут работать, а будут только читать люди. То есть уже не нужные данные.

                        Нет, не такие. А к чему вопрос?

                        статья про код. Значит в этой статье нет данных, подверженых коррозии "национально-специфичному форматированию". Значит тут запятая неуместна.


            1. Elrond16
              27.11.2023 10:37
              +1

              По-моему, надо избавляться от жесткой привязки семантики к представлению в коде. Все нормальные библиотеки работы с данными должны иметь возможность выбирать читать как запятые, так и точки по выбору (кроме того, что хранить численные данные в текстовом представлении — вообще плохая практика в науке, от которой стоит отказываться). При написании статьи в LaTeX можно пользоваться пакетом siunitx, в котором замена точки на запятую делается одним параметром output-decimal-marker или вообще заданием локали для пакета.

              По-моему, легаси — это как раз игнорирование языкового и типографического разнообразия. Как раньше все только с ASCII работали и предполагали, что других символов кроме латиницы в мире нет, а сейчас отсутствие поддержки юникода — дурной тон.


              1. adeshere
                27.11.2023 10:37
                +1

                По-моему, легаси — это как раз игнорирование языкового и типографического разнообразия. Как раньше все только с ASCII работали и предполагали, что других символов кроме латиницы в мире нет, а сейчас отсутствие поддержки юникода — дурной тон.

                Вы в каком-то идеальном мире живете.

                В науке ситуация совершенно другая.

                Реальная ситуация такова: есть один человек, и у него условные 10 часов, за которые можно либо повысить точность расчета в 10 раз (и получить нечто выше мирового уровня), либо вместо этого оформить мультиязычный интерфейс к своей программе (но сами результаты расчетов будут никому не нужны).

                Что Вы выберете на месте такого сотрудника?


                1. DreamingKitten
                  27.11.2023 10:37

                  Вы в каком-то идеальном мире живете.

                  Мы его пишем.

                  Что Вы выберете на месте такого сотрудника?

                  Выберу место работы, где руководство не вынуждает стоять перед подобными дилеммами.


                  1. adeshere
                    27.11.2023 10:37

                    Мы его пишем.

                    Искренне желаю Вам удачи в этом деле.

                    Но, столь же искренне сомневаюсь, что такой мир возможен. Если за столько лет даже столь массовый продукт, как Exell, не научился снимать с юзера все проблемы с совместимостью локалей и версий, то что уж об остальных говорить... Ведь даже если кто-то сумеет решить все такие проблемы в своем заповеднике, есть же еще огромный остальной мир, с которым придется как-то взаимодействовать, и который на эти стандарты либо забьет, либо примет свои, либо будет кишеть нестандартизированным легаси...Тут как с календарем: имеющий легаси-календарь ужасен, уродлив и неудобен чуть меньше, чем всем. Гораздо более идеальных календарей - пруд пруди. И тем не менее шансы, что один из них будет принят ВСЕМ миром, пока что строго равны нулю...

                    Выберу место работы, где руководство не вынуждает стоять перед подобными дилеммами.

                    Совет хороший, но годится далеко не для всех. В соседней ветке я подробно рассказал о своем коллеге. У него такого выбора в принципе не было, так как дилемма исходит вовсе не от начальства, а от гораздо более фундаментальных вещей. И, глядя на интерфейс других программ расчета приливов, у меня складывается впечатление, что в фундаментальной (а не прикладной) науке это достаточно типичная ситуация.


                1. Elrond16
                  27.11.2023 10:37

                  При использовании сторонних протестированных инструментов вместо собственных костылей писать более совместимые вещи еще и выходит быстрее. Та же поддержка юникода распространилась больше за счет языков и библиотек, нативно поддерживающих юникод, а не копания в том, как юникод устроен. И уж точно тот факт, что сотрудник вручную через CSV данные пишет и читает без автоматического экранирования, не был бы ему плюсом.

                  Я сам всю жизнь в науке работаю, понимаю, сколько в ней уродливого софта, и что не всегда есть возможность избежать его использования. Но это повод хотя бы не стремиться к лучшему.


        1. event1
          27.11.2023 10:37
          +12

          А в области, связанной с программированием то, что поддерживается компиляторами.

          Да и от использования русского языка тоже надо отказаться. Ведь компиляторы его не поддерживают.

          Запятая пришла к нам со времён рукописного текста - её проще ставить и заметить.

          А англичане-то не знали. Пользовались точкой и мучились, бедные. Если что вот тут освещена история вопроса


        1. Ugli
          27.11.2023 10:37
          +2

          А уж как устарела имперская система мер!
          С ней будем бороться?


          1. Bluewolf
            27.11.2023 10:37
            +1

            Но ведь она действительно устарела и бороться с ней активнее очень было бы неплохо!


            1. Ugli
              27.11.2023 10:37

              Да!
              Представляю себе новый корпус SOIC8(EU) с расстоянием между ножками 1,5мм вместо 1,27))


              1. ksbes
                27.11.2023 10:37
                +3

                А почему бы и не 1,27? Главное, чтобы это было именно 1,27 мм, а не 1/20''


                1. Ugli
                  27.11.2023 10:37
                  +1

                  В этом нет элемента борьбы!


                  Если серьёзно - я в своём диайвай постоянно развожу бардак, когда забываю переключать сетки. Но тут вопрос не смены системы или точек с запятыми, а моего личного головотяпства.


              1. johnfound
                27.11.2023 10:37

                Очень много современных SMD элементы имеют размеры закругленные по метрической системе.

                А кстати, 1.27мм это именно метрический размер. Если бы написали 50mils, это было бы по имперски.


                1. Ugli
                  27.11.2023 10:37

                  Сейчас как бы милы точно соответствуют миллиметрам, это раньше был разброс в точных значениях.

                  Соотношение дюйма и сантиметра (из Вики):

                  • 1819 год — 1000000/393694 см ≈ 2,5400438 см;

                  • 1895 год — 2,5399978 см;

                  • 1922 год — 2,5399956 см;

                  • 1932 год — 2,5399950 см;

                  • 1947 год — 2,5399931 см.

                  • с 1958 года приравнивается точно к 2,54 см.

                  Я писал именно о приведении к кратности к миллиметру и его десятичным частям.
                  Хотя ясно что уже столько выпущено деталей что никто переделывать привязку к дюйму уже не будет.


              1. Goron_Dekar
                27.11.2023 10:37
                +1

                А вы не задумывались, почему все более современные SMD компоненты уже названы и стандартизированы в миллиметрах?


                1. Ugli
                  27.11.2023 10:37

                  Я сварщик ненастоящий, отличить современные SMD от несовременных часто не в состоянии. Поэтому просто вижу разнобой в единицах измерений в даташитах.
                  Итого - нет, не задумывался)


          1. Goron_Dekar
            27.11.2023 10:37
            +2

            Да. И местами удачно. Фарм индустрия в США потихоньку переезжает на метрику, и это спасает кучу человекочасов, причём весьма квалифицированных, то есть делает производство дешевле, выгоду болшьше а продукт доступнее.


  1. NeoCode
    27.11.2023 10:37
    +2

    NF4 это какая-то хитрая разновидность формата с фиксированной запятой? Судя по числам, там разные расстояния между соседними числами в положительной и отрицательной областях. Это кстати оправдано?


  1. lamerok
    27.11.2023 10:37
    +1

    На PIC помню еще был float24


    1. iBuilder
      27.11.2023 10:37
      +1

      А у DSP серии ADSP от AD поддерживался 40-bit float


  1. Melirius
    27.11.2023 10:37
    +4

    А физики смотрят на этот "пир духа" и тихонько посмеиваются, делая ставки на то, когда до ML-щиков дойдёт понятие "численной устойчивости метода".


    1. ShadowTheAge
      27.11.2023 10:37
      +4

      Данные форматы, начиная с half и ниже, предназначены для хранения данных а не для вычислений. Операций вычисления даже с half я с ходу не могу сказать кто вообще поддерживает, может быть какие то железки специально для ML, GPU хоть и поддерживают half но только для уменьшения bandwidth, вычисления делаются в single.


      1. beeruser
        27.11.2023 10:37
        +3

        GPU хоть и поддерживают half  но только для уменьшения bandwidth, вычисления делаются в single.

        Это не правда. Некоторые карты Nvidia и любые карты AMD после Поляриса, поддерживают нативные вычисления в FP16. Ровно как и GPU Apple.

        https://www.techpowerup.com/gpu-specs/geforce-rtx-2080-ti.c3305

        RTX2080ti

        FP16 (half)26.90 TFLOPS (2:1)

        FP32 (float)13.45 TFLOPS

        FP64 (double)420.2 GFLOPS (1:32)

        Radeon RX7800

        https://www.techpowerup.com/gpu-specs/radeon-rx-7800-xt.c3839

        FP16 (half)74.65 TFLOPS (2:1)

        FP32 (float)37.32 TFLOPS

        FP64 (double)1,166 GFLOPS (1:32)

        32-битный регистр содержит две 16-битных половинки. Можно работать как с одной половинкой, так и сразу с двумя, командами типа V_PK_MUL_F16 и другими.

        https://gpuopen.com/learn/first-steps-implementing-fp16/


      1. sappience
        27.11.2023 10:37

        Для числовых форматов с малой разрядностью весь набор операций можно реализовать таблично. Если получившаяся табличка влезет в кэш, то получится весьма быстро и без поддержки CPU.


    1. uszer
      27.11.2023 10:37

      Это же для neural net используется. Здесь вычислительная устойчивость достигается другими методами, да и вообще нейросеть, для каких-то задач может быть вообще не применима. Для задач, решаемых с использованием нейросетей, требуется другая устойчивость - "робастность".


    1. aamonster
      27.11.2023 10:37

      Я думал, что их применяют там, где устойчивость заведомо есть.


  1. johnfound
    27.11.2023 10:37
    +7

    Любой алгоритм на числах с плавающей точкой можно переписать на целых числах. И когда речь идет о 4-битных числах, то сам бог велел. И проще и нагляднее и быстрее.

    Не знаю почему городят эти пляски с плавающей точкой... Если кто знает, пусть объяснит.


    1. Barabashkad
      27.11.2023 10:37
      +1

      как ответили выше , 4-битные числа экономят место для хранения
      в целыс числах всегда занимает больше места


      1. johnfound
        27.11.2023 10:37
        +3

        Я имел ввиду 4-битные целые числа. Они занимают точно такое место и принимают точно те же 16 значения.


        1. ksbes
          27.11.2023 10:37
          +2

          За счёт неравномерной сетки получается лучше перемножать числа < 1. Т.е. (1/16)^2 на целых не вычислишь, а на таких флоатах - вычислишь, хоть и с погрешностями.


          1. johnfound
            27.11.2023 10:37

            Вы не прочитали мой первый пост по теме. Если алгоритмы написаны с целыми числами, то нужды вычислять (1/16)^2 не возникнет. Там будут только целые числа. А кстати, покажите-ка как с 4-битными числами запишете 1/16 вычислите результат (хоть с большей разрядностью) и запишете результат? Получится 0*0 = 0.


        1. Barabashkad
          27.11.2023 10:37

          ок, давайте пример из статьи переведем в целые числа
          0.0000, 0.0052, 0.6667, 1.0000, 0.3333, 0.5000, 0.1667, 0.2500, 0.0000, -0.0052, -0.6667, -1.0000, -0.3333, -0.5000, -0.1667, -0.2500
          ...


          1. johnfound
            27.11.2023 10:37

            Честно говоря, я не знаю как эти числа в статье были получены. Покажите двоичный код. Два нуля вижу. Но где 2хNaN и 2хInf?

            А вообще-то если примем, что должно перекрывать диапазон -1..+1, то в инт, просто надо разделить число на 8: получим диапазон от -8/8 .. +7/8 = -1 .. 0.875; Единица младшего разряда будет 1/8 = 0.125;


    1. iBuilder
      27.11.2023 10:37
      +1

      С целыми у вас не получится динамический диапазон сделать как у вещественного, если вы про реализацию дробных чисел целыми, как в DSP целочисленных делают. А если в уме начнете множители учитывать, так получите свою реализацию вещественного формата на 4 бита, а не целочисленную математику, то-же самое будет у вас, но чуть по другому.


      1. johnfound
        27.11.2023 10:37
        +1

        Наоборот. У 4-битового числа с плавающей точкой все-равно есть два нуля, две бесконечности и два NaN. То есть использовать можно 11 значений. У 4-битового целого есть все 16 значения и динамический диапазон у него больше.


        1. iBuilder
          27.11.2023 10:37

          Про "NaN" - судя из теста статьи, в этом формате этого нет. Беглый поиск не дал точное описание формата, поэтому делаю предположение, что NaN там и не будет, а будет контроль переполнения, тогда в результате переполнения будет максимум, как делают в ЦОС и DSP процессорах. И в плавающей точке эти проверки будут делаться автоматически, а для целой точки вам придется или это как-то контролировать и получите примерно то-же самое, если начнете обставлять операции проверками.

          Целые числа, например, используют, когда сетку засовывают, например, в FPGA - там заранее при анализе смотрят на диапазоны и где можно - переводят в целую точку.


    1. zartdinov
      27.11.2023 10:37
      +4

      Точно не скажу, но основная хитрость в том, что значения распределены не равномерно, ближе к нулю значений становится больше (можно заметить на графике синуса). То есть точность повышается при стремлении к нулю. А с точки зрения байтов, да, никакой разницы.


      1. adeshere
        27.11.2023 10:37
        +1

        @iBuilder: С целыми у вас не получится динамический диапазон сделать как у вещественного

        @zartdinov: Точно не скажу, но основная хитрость в том, что значения распределены не равномерно, ближе к нулю значений становится больше

        Все верно... но бывают и контрпримеры. Например, у нас часто измерительный датчик прикручен ко входу 16-битного АЦП. То есть значения на выходе по сути получаются целые. Хотя по физическому смыслу они real. Только этот real надо еще сперва вычислить, прибавив к базовому смещению произведение выходного сигнала на цену деления одного разряда АЦП. Например, нулевому значению АЦП может соответствовать число "100", а максимальному - "1000". И дальше уже не важно, преобразуются ли данные после АЦП в real сразу же, или это делается когда-то потом. Количество возможных значений все равно ограничено разрядностью АЦП.

        Для хранения таких данных равномерная сетка подойдет значительно лучше, чем динамическая. Важно лишь, чтобы она заполняла целевой диапазон и только его. Поэтому мы еще в 1989 году сделали в своей программе формат real*16. Правда, не для расчетов, а для хранения данных в БД.

        На практике для работы с такими сигналами сперва задается максимальный диапазон изменения физической величины (обычно это делает юзер при оформлении паспорта ряда). Затем этот диапазон делится на 2^16 уровней (в том числе пара служебных - для пропусков и т.п.), и в базу заносится не само real-число, а номер ближайшего уровня. При распаковке из базы выполняется обратное преобразование, так что клиент всегда видит значение в физических единицах. Например, для хранения измерений атмосферного давления, которые изменяются от 900 до 1100 мбар, неравномерная сетка значений (более плотная вблизи нуля) заведомо хуже, чем равномерная, но покрывающая только диапазон 900-1100.

        А спустя много лет почти такой же формат реализовала в своей системе фирма Zetlab.

        Но, конечно, это не универсальный «minifloat», а узкоспециализированный, так как кроме массива данных надо где-то отдельно хранить еще и границы диапазона.

        P.S. Что интересно, сейчас в наших базах геофизических рядов данных этот 16-битный формат - основной, так как лишь очень немногие системы измерений действительно дают такую точность, при которой 16-битная дискретизация уже недостаточна.


        1. aamonster
          27.11.2023 10:37

          Вообще обычно это называется fixed point (формат с фиксированной точкой). Или вы что-то другое сделали?


          1. adeshere
            27.11.2023 10:37

            обычно это называется fixed point (формат с фиксированной точкой). Или вы что-то другое сделали?

            Да, это похоже на fixed point, но не совсем. Так как у нас диапазон строится не от нуля, а в произвольном месте шкалы. Например, сопротивление глубокого горизонта может меняться от 500 до 550 ом*м. Классический 2-байтовый fixed point, если я ничего не путаю, позволит сохранять такие значения с одним-двумя точными десятичными знаками: если полный диапазон -32768..+32767, то 510.12345 превратится в 510.1, ну или в 510.12 при наличии некоторых ухищрений. А в нашем случае соседние уровни будут идти с шагом 0.001: 510.123, 510.124 и т.д. Немного, но все-таки поточнее.

            Мне видимо просто повезло, что когда я эту систему придумывал, то про fixed point не знал ничего. Хотя она в то время (1988год) уже эксплуатировалась вовсю...


        1. sim31r
          27.11.2023 10:37
          +3

          Например, для хранения измерений атмосферного давления, которые изменяются от 900 до 1100 мбар, неравномерная сетка значений (более плотная вблизи нуля) заведомо хуже, чем равномерная, но покрывающая только диапазон 900-1100.

          Ну нет. Ноль при измерении атмосферного давления принимаем за 1000 мбар, а отклонение упаковываем во float8 например, как-раз и знак пригодится. Тогда самая большая часть будет вблизи значений с максимальной точностью, а редкие вылеты за норму будут более грубо представлены. Как на картинке синусоиды в статье. Плюс масштабирующий коэффициент чтобы +- 100 мбар укладывались в диапазон float8.

          Обычно как-то так Y= x0 + k*x

          В каком-то случае этого будет недостаточно и потребуется масштабирование нелинейное, полиномом или таблично. Но это теоретически, вряд ли это в рамках инженерной задачи потребуется экономия на битах. Проще хранить избыточные данные, float64 и включить сжатие данных, хоть на уровне файловой системы.

          где-то отдельно хранить еще и границы диапазона

          Еще где-то хранится порог аварийного срабатывания например и желаемой значение для систем автоматического регулирования. Но это вроде совсем другое всё. Именно к структуре числа не относится, это интерпретация значения и уточнение контекста в конкретном применении данных.


          1. adeshere
            27.11.2023 10:37
            +2

            Ну нет. Ноль при измерении атмосферного давления принимаем за 1000 мбар, а отклонение упаковываем во float8 например, как-раз и знак пригодится

            Да, мысль интересная (а в идеальном сферическом мире так и вовсе должна быть за аксиому). Но у нас все проще и прозаичнее: на выходе датчика стоит 16-битный АЦП. Который дает 2^16 уровней. Вот они-то фактически и хранятся в БД.

            А весь смысл в том, что клиент получает из базы обычные real-значения в физических единицах. Которые сразу можно в арифметику. Она тогда была строго 32-битная. Это же все на 8086 делалось, а 286 для нас революцией стал. Там надо было, чтоб просто и быстро. Какие уж там избыточные данные...

            А  сжатие данных - оно в любом варианте работает почти одинаково.

            P.S. Но что касается современной эпохи, то идея с заменой 16-битной равномерной сетки на 8-битную неравномерную (real*8) - рабочая. Наверно и правда сделаем такой вариант хранения данных, как опцию. Технически при нашей архитектуре это вообще без проблем (правда, обратная совместимость нарушится). Так что спасибо за наводку ;-) Ради таких идей и нужны прогулки по комментариям ;-)

            Но все же вряд ли такой формат будет широко востребован, так как у нас очень редко сигнал имеет фиксированный уровень, вблизи которого группируется основная масса значений, и "хвосты", где точность высокая не нужна. Большинство сигналов постоянно "гуляет" вверх-вниз, почти равномерно заполняя треть или даже половину диапазона. Например, у сопротивления основная компонента сигнала - это сезонный ход. А у синусоиды, как известно, функция распределения имеет два горба по краям и провал в середине. Ну и дрейфы, конечно, из-за которых Ф.Р. размазывается... А наклоны - так и просто дрейфуют постоянно туда-сюда. То есть, возле границ диапазона наша равномерная сетка и правда загущена (так как там всегда нужен какой -то запас, а значений немного), но вот понятие "центр" у нас очень-очень растянуто.

            UPD. Черт возьми! Хотел за ценный совет

            плюсануть карму

            а оказалось, что она уже давно заплюсована...Может, это кто-нибудь из неравнодушных за меня сделает?


    1. iShrimp
      27.11.2023 10:37

      Может, там используется такая функция активации с длинными хвостами, что нужен большой диапазон чисел. Хотя, имхо проводить вычисления над 4-битным Float - это уже перебор, для 4 бит лучше таблица поиска типа NF4, но специально оптимизированная под данную нейросеть.

      Точнее, две таблицы: для распаковки 4bit->8bit и для упаковки 8bit->4bit, а вычисления проводить в целом 8bit или 16bit формате.


      1. sim31r
        27.11.2023 10:37
        +1

        Может, там используется такая функция активации с длинными хвостами, что нужен большой диапазон чисел.

        В статье не хватает сравнения действительно с обычными целочисленными 4-битными числами. На сколько увеличится ошибка. Float все же не идеальный формат и дает более грубое представление значения по краям диапазона. Может разница будет в 10-20% всего.


    1. leshabirukov
      27.11.2023 10:37
      +2

      Int-ы хороши для чего-то, измеряемого в штуках, а у нас нечто непрерывное, тяготеющее к диапазону [0(или-1)..1]. Есть такая вещь, как фиксированная точка, но это уже не так наглядно, а динамический диапазон много меньше. Кстати, где вы поставите точку? Либо вы её позицию стандартизируете - зафиксируете для всей индустрии, (еще хуже с диапазоном), либо проще превращается в боль с отслеживанием и согласованием её в контексте.
      А на счет быстрее (производительнее?) - не так велик там выигрыш, даже для здоровых матричных молотилок. (А самые здоровые матричные молотилки еще и обучать должны, а это градиенты, а градиенты к динамическому диапазону чувствительны страшно.)
      Я в своё время тоже недоумевал, почему про квантизацию на каждой конфе говорят, и так мало делают.


      1. Inkor
        27.11.2023 10:37
        +1

        Ну мало делают это смотря где. Если embedded разработка и нужно что бы работало на какой-нибудь платке да еще и в реальном времени, то делают часто.
        Опенсурсные энтузиасты LLM-ок тоже активно юзают и фактически развивают сейчас это направление. Ибо модели огромные и всем охота поиграться на домашних видеокартах, а не лезть в облака за A100 какой-нибудь. И тут весь цвет общества применяется. 16 бит для весов это уже стандарт, 8 бит и 4 бита легко. Плюс всякие GPTQ и AWQ методы.
        Другое дело что это у энтузиастов и всяких ребят решивших срубить денег как хостинги для опенсусрных моделей. Применяют ли все это большие ребята типа OpenAI или Anthropic в продакшене, черт его знает.


        1. leshabirukov
          27.11.2023 10:37

          \ Ну мало делают это смотря где

          Сейчас то да, я имел в виду скорее "делали", вопрос поднимался давно, еще до начала всей этой весны.


  1. Inkor
    27.11.2023 10:37
    +1

    4 бита еще не самая большая наркомания в машинном обучении. Как вам 3 и даже 2 бита? Правда тут конечно уже не плавающая точка. И квантизация не везде проходит до 2 битов, но тем нем менее. Причем активно используется, когда дело касается опенсурсных LLM. Поддерживается библиотекой transformers.