FP32, FP16, BF16 и FP8 — разбираемся в основных типах чисел с плавающей запятой

от автора

Привет, Хабр! Сегодня давайте поговорим о том, как современные вычисления на GPU стали более гибкими и эффективными благодаря различным форматам чисел с плавающей запятой (FP64, FP32, FP16, BFLOAT16 и FP8). Эти форматы не просто числа — за каждым из них стоит конкретная область применения. В разных ситуациях мы сталкиваемся с задачами, где важны либо скорость, либо точность, и правильно выбранный тип floating point помогает оптимизировать ресурсы. Давайте разберём всё это на примерах и поймём, в каких задачах каждый из этих форматов будет наиболее полезен.

Введение

В предыдущей статье мы рассказали о истории появления чисел с плавающей запятой, но лишь частично рассказали о их применении на практике. Сегодня же мы хотим сконцентрировать ваше внимание на каждом из популярных форматов вычислений и рассказать, почему же существует такое количество разнообразных форматов вычислений. Для корректного формирования выборки типов вычислений, мы будем обращаться к спецификации топового ускорителя для обучения ИИ — Nvidia Tesla H100.

FP64: максимальная точность для научных расчётов

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

Задача: Рассмотрим задачу численного интегрирования для расчёта траектории космического объекта, движущегося под воздействием гравитации Земли. Для таких задач крайне важно использовать максимальную точность, чтобы не допустить отклонений в расчётах, которые могут привести к неправильному предсказанию орбиты.

Пример кода для численного интегрирования с использованием FP64:

import torch  # Положение и скорость спутника initial_position = torch.tensor([7.0e6, 0.0, 0.0], dtype=torch.float64) initial_velocity = torch.tensor([0.0, 7.12e3, 0.0], dtype=torch.float64) mass_earth = 5.972e24 g = 6.67430e-11  # Функция для вычисления ускорения def compute_acceleration(position, mass, g=6.67430e-11):    distance = torch.norm(position)    return -g * mass / distance**3 * position  # Метод Рунге-Кутты для численного интегрирования def runge_kutta_step(position, velocity, mass, time_step):    k1_v = compute_acceleration(position, mass) * time_step    k1_x = velocity * time_step    k2_v = compute_acceleration(position + k1_x / 2, mass) * time_step    k2_x = (velocity + k1_v / 2) * time_step    return position + (k1_x + 2 * k2_x) / 3, velocity + (k1_v + 2 * k2_v) / 3  # Применение метода time_step = 1.0  # шаг времени в секундах position, velocity = runge_kutta_step(initial_position, initial_velocity, mass_earth, time_step)  print(f"Положение: {position}, Скорость: {velocity}")

Зачем здесь нужен FP64? В этой задаче важно, чтобы даже минимальные погрешности не привели к значительным отклонениям в расчётах, поскольку расчёт орбиты требует максимальной точности. FP64 позволяет минимизировать ошибки округления и обеспечивает высокую точность при выполнении численных методов, таких как метод Рунге-Кутты.

FP32: баланс между точностью и скоростью

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

Задача: Умножение матриц для обработки изображений в реальном времени. В таких задачах требуется быстрое выполнение вычислений, а точность FP32 достаточна для получения качественных результатов.

Пример кода для умножения матриц с использованием FP32:

import torch   # Инициализация матриц в формате float32 matrix_a = torch.tensor([[1.0, 2.0], [3.0, 4.0]], dtype=torch.float32) matrix_b = torch.tensor([[5.0, 6.0], [7.0, 8.0]], dtype=torch.float32)   # Выполнение умножения матриц result = torch.matmul(matrix_a, matrix_b)   print(f"Результат умножения матриц с FP32: \n{result}")

Зачем здесь нужен FP32? В ряде задач, связанных с рендерингом или обработкой графики, критически важно выполнять расчёты в реальном времени, например, при создании графических эффектов или обработке видео. FP32 обеспечивает хорошее соотношение между точностью и производительностью. Конечно, крайне маловероятно что кто-то будет использовать Python для задач требующих исполнения в реальном времени, но для однородности он тут выбран как и для остальных примеров, хотя C/C++/Rust/Zig подошли бы лучше.

FP16: ускорение обработки данных

FP16 — это 16-битный формат, который позволяет значительно ускорить вычисления за счёт уменьшения точности, но без существенного ущерба для качества результата. Этот формат активно используется в задачах машинного обучения и нейросетей, где важна высокая скорость обработки больших объёмов данных.

Задача: Обучение нейросети для классификации изображений. FP16 позволяет ускорить тренировку модели без значительной потери качества.

Пример кода для работы с FP16:

import torch import torch.nn as nn import torch.optim as optim  # Простая нейросеть для классификации class SimpleNet(nn.Module):    def __init__(self):        super(SimpleNet, self).__init__()        self.fc1 = nn.Linear(784, 128)        self.fc2 = nn.Linear(128, 10)     def forward(self, x):        x = torch.relu(self.fc1(x))        return self.fc2(x)  # Инициализация данных и модели device = torch.device("cuda" if torch.cuda.is_available() else "cpu") model = SimpleNet().to(device, dtype=torch.float16) optimizer = optim.Adam(model.parameters(), lr=0.001)  # Фиктивные данные для обучения data = torch.randn(64, 784, dtype=torch.float16).to(device) target = torch.randint(0, 10, (64,), dtype=torch.long).to(device)  # Прогон обучения optimizer.zero_grad() output = model(data) loss = nn.CrossEntropyLoss()(output, target) loss.backward() optimizer.step()  print(f"Потери: {loss.item()}")

Зачем здесь нужен FP16? При обучении больших нейросетей на огромных датасетах время обработки играет решающую роль. FP16 позволяет в несколько раз ускорить процесс обучения на GPU, уменьшив объём занимаемой памяти и снизив потребление ресурсов. Это особенно полезно при обучении глубоких моделей с большими объёмами данных.

BFLOAT16: оптимизация для инференса

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

Задача: Инференс модели для обработки изображений с использованием BFLOAT16 для ускорения вычислений.

Пример кода для инференса с использованием BFLOAT16:

import torch  # Инициализация данных с использованием BFLOAT16 input_data = torch.randn(64, 784, dtype=torch.bfloat16).cuda()  # Фиктивная модель для инференса class SimpleInferenceNet(nn.Module):    def __init__(self):        super(SimpleInferenceNet, self).__init__()        self.fc = nn.Linear(784, 10)     def forward(self, x):        return self.fc(x)  # Выполнение инференса model = SimpleInferenceNet().cuda().bfloat16() output = model(input_data)  print(f"Результат инференса: {output}")

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

FP8: максимальная производительность для инференса

Представление числа 0.3952 различными типами чисел с плавающей запятой.

Представление числа 0.3952 различными типами чисел с плавающей запятой.

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

Задача: Выполнение инференса с использованием FP8 для распознавания объектов на изображении.

Пример кода:

import torch import torch.nn as nn # Import the torch.nn module  # Пример симуляции работы с FP8 def to_fp8(tensor):    return torch.clamp(tensor, min=-128, max=127).to(torch.float32)  # Инициализация данных input_data = torch.randn(64, 784).cuda() input_data_fp8 = to_fp8(input_data)  # Простейшая модель для инференса class SimpleFP8Net(nn.Module):    def __init__(self):        super(SimpleFP8Net, self).__init__()        self.fc = nn.Linear(784, 10)     def forward(self, x):        return self.fc(x)  # Выполнение инференса model = SimpleFP8Net().cuda() output = model(input_data_fp8)   print(f"Результат с использованием FP8: {output}")

Зачем здесь нужен FP8? FP8 идеально подходит для задач, где точность может быть снижена в пользу максимальной скорости обработки данных. Это актуально в инференсе для систем искусственного интеллекта, где нужно мгновенно обрабатывать огромные объёмы данных с минимальными затратами ресурсов.

Заключение

Каждый тип float — будь то FP64, FP32, FP16, BFLOAT16 или FP8 — имеет своё применение и должен выбираться в зависимости от задачи. FP64 — для научных расчётов, FP32 — для баланса между производительностью и точностью, FP16 — для обучения нейросетей, а BFLOAT16 и FP8 — для инференса. Современные ускорители Nvidia Tesla, Radeon Instinct или Intel GPU Max поддерживают все эти форматы, что позволяет вам максимально эффективно использовать мощь GPU для каждой конкретной задачи.

А какой тип float используете вы в своих проектах и почему? Делитесь в комментариях, будет интересно обсудить!

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

Для чего вы используете Floating point?

50% Научные вычисления6
41.67% Машинное обучение и искусственный интеллект5
33.33% Симуляции и моделирование4
33.33% Компьютерная графика4
25% Свой вариант в комментариях3

Проголосовали 12 пользователей. Воздержались 6 пользователей.

ссылка на оригинал статьи https://habr.com/ru/articles/847068/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *