Документация / РусскийИсходный текст ↗

ruRAND Руководство пользователя

English | 简体中文 | 日本語 | Deutsch | Русский

Вычислительные библиотеки · Среда выполнения API · 中文

ruRAND генерирует тензоры устройств с равномерным, нормальным распределением и распределением Бернулли. Используйте rurand::tensor для выделения новых тензоров или одноименных функций в корне крейта для заполнения существующей памяти устройства.

1. Настройте зависимости

Пакет Cargo — ruRAND; его имя для импорта в Rust — rurand. Функция tensor включает тензорные интерфейсы устройств. В этой конфигурации каталог приложения размещается рядом с исходным каталогом RUDA. См. раздел Начало работы для настройки NVIDIA.

[dependencies]
rurand = { package = "ruRAND", path = "../RUDA/ruRAND", default-features = false, features = ["std", "tensor"] }
ruda-core = { path = "../RUDA/ruda-core", default-features = false, features = ["std", "tensor-host-data"] }
ruda-kernel = { path = "../RUDA/ruda-kernel", default-features = false, features = ["frontend-std", "device-tensor"] }
ruda-driver-cuda = { path = "../RUDA/ruda-driver-cuda", default-features = false, features = ["std"] }

2. Генерация случайных тензоров

Этот полный src/main.rs генерирует тензоры F32 формы [2, 3] и воспроизводит вызов, сбрасывая начальное число. Запустите cargo run из каталога приложения:

use ruda_core::tensor::DType;
use ruda_driver_cuda::{CudaDevice, CudaRuntime};
use ruda_kernel::tensor::readback::into_data_sync;
use rurand::tensor::{
    random_bernoulli, random_like_uniform, random_normal, random_uniform,
};

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let device = CudaDevice::default();
    rurand::seed(42);
    let uniform = random_uniform::<CudaRuntime>([2, 3].into(), &device, 0.0, 1.0, DType::F32);
    let first = into_data_sync(uniform.clone()).to_vec::<f32>()?;

    rurand::seed(42);
    let repeated = random_uniform::<CudaRuntime>([2, 3].into(), &device, 0.0, 1.0, DType::F32);
    let repeated = into_data_sync(repeated).to_vec::<f32>()?;
    assert_eq!(first, repeated);
    assert!(first.iter().all(|&x| (0.0..1.0).contains(&x)));

    let normal = random_normal::<CudaRuntime>([2, 3].into(), &device, 0.0, 1.0, DType::F32);
    let mask = random_bernoulli::<CudaRuntime>([2, 3].into(), &device, 0.25, DType::F32);
    let noise = random_like_uniform(&uniform, -0.1, 0.1, DType::F32);

    let mask = into_data_sync(mask).to_vec::<f32>()?;
    assert!(mask.iter().all(|&x| x == 0.0 || x == 1.0));
    println!("uniform={first:?}");
    println!("normal={:?}", into_data_sync(normal).to_vec::<f32>()?);
    println!("mask={mask:?}");
    println!("noise={:?}", into_data_sync(noise).to_vec::<f32>()?);
    Ok(())
}

random_like_uniform берет из эталона только форму и устройство. Он выделяет новый тензор без изменения и копирования опорных значений. Его последний аргумент явно выбирает выход dtype.

random_bernoulli возвращает нули и единицы в выбранном dtype, а не автоматически созданную логическую маску. В этом примере возвращаются значения F32 0.0 и 1.0.

3. Параметры и распределения

Первые три тензорные функции имеют общие параметры shape: Shape, device: &R::Device и последний параметр dtype: DType. Возвращенный RudaTensor<R> имеет указанную форму dtype и устройство.

Порядок функций и параметров Параметры распределения
random_uniform(shape, device, lower_bound, upper_bound, dtype) f32 нижняя и верхняя границы
random_normal(shape, device, mean, std, dtype) f32 среднее и стандартное отклонение; стандартное значение не является отклонением
random_bernoulli(shape, device, probability, dtype) f32 вероятность возврата 1
random_like_uniform(&reference, lower_bound, upper_bound, dtype) Базовый тензор, границы f32, выход dtype

Предоставьте конечные параметры, соответствующие распределению: нижняя граница ниже верхней границы, неотрицательное нормальное стандартное отклонение и вероятность Бернулли в [0, 1]. Эти интерфейсы не проверяют эти диапазоны через Result; не используйте недопустимые параметры для запроса возврата ошибки.

При генерации однородного результата сначала создается F32 u ∈ [0, 1), вычисляется lower_bound + (upper_bound - lower_bound) × u, а затем выполняется приведение к выходным данным dtype. Случай F32 [0, 1) исключает 1. Масштабирование до других интервалов или преобразование в более низкую точность может привести к округлению значения вблизи верхней границы этой границы.

Обычное генерирование использует преобразование Бокса-Мюллера FP32 перед приведением к выходу dtype. Выбор хранилища F64 не увеличивает внутреннюю точность случайных чисел с плавающей запятой. Бернулли сравнивает u < probability, поэтому вероятность 0 дает все нули, а вероятность 1 дает все единицы.

4. Начальные значения и порядок вызова

rurand::seed(seed: u64) сбрасывает случайное состояние общего хоста. Каждый вызов поколения извлекает новые семена из этого состояния и продвигает его:

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

5. Заполните существующий буфер.

Используйте функции запуска корневого каталога для повторного использования выходного распределения. Эта функция берет уже выделенный тензор устройства F32, записывает случайные значения в его хранилище и возвращает его:

use ruda_kernel::{
    dsl::{Runtime, prelude::LaunchError},
    tensor::RudaTensor,
};

fn fill_uniform<R: Runtime>(
    output: RudaTensor<R>,
    lower: f32,
    upper: f32,
) -> Result<RudaTensor<R>, LaunchError> {
    rurand::random_uniform(
        &output.client,
        lower,
        upper,
        output.clone().binding(),
        output.dtype.into(),
    )?;
    Ok(output)
}

Эта функция не вызывает тензорный распределитель. Другие дескрипторы, совместно использующие это хранилище, также наблюдают за записью. Соответствующими функциями для нормальных значений и значений Бернулли являются rurand::random_normal(&client, mean, std, binding, dtype) и rurand::random_bernoulli(&client, probability, binding, dtype).

Функции тензорного уровня вызывают панику, если при запуске возвращается ошибка; базовая точка входа выше возвращает Result<(), LaunchError>. Успешная отправка не означает, что выполнение GPU завершено. Считайте тензор обратно или дождитесь синхронизации клиента. into_data_sync паникует при сбое чтения; используйте асинхронный into_data(tensor).await для распространения ошибок обратного чтения.

API Ссылка: Тензорные интерфейсы, Исходное состояние, Распределения.