Найти в Дзене
Один Rust не п...Rust

Cow (Clone-on-Write) для ML

Cow (Clone-on-Write) - тип, который позволяет переиспользовать данные без копирования, пока это возможно. ML-проекты работают с большими объёмами данных, где лишние аллокации и копии сильно бьют по производительности (особенно на inference). Cow даёт «бесплатную» оптимизацию: пишешь код так, будто у тебя всегда owned-данные, а Rust копирует только когда действительно нужно изменить. Это делает код проще и быстрее, чем ручное ветвление на &str vs String или &[T] vs Vec. Принимаем входной текст как &str, разбиваем его на токены и применяем нормализацию (приведение к нижнему регистру) только когда это действительно нужно. Благодаря Cow мы избегаем лишних аллокаций для токенов, которые остаются без изменений. use std::borrow::Cow; fn tokenize_and_normalize(text: &str) -> Vec<Cow<str>> { text .split_whitespace() // простая токенизация по пробелам .map(|token| { // Пример нормализации: приводим к нижнему регистру только если есть заглавные буквы if token.chars().any(char::is_uppercase) { Cow
Оглавление

Cow (Clone-on-Write) - тип, который позволяет переиспользовать данные без копирования, пока это возможно.

ML-проекты работают с большими объёмами данных, где лишние аллокации и копии сильно бьют по производительности (особенно на inference). Cow даёт «бесплатную» оптимизацию: пишешь код так, будто у тебя всегда owned-данные, а Rust копирует только когда действительно нужно изменить. Это делает код проще и быстрее, чем ручное ветвление на &str vs String или &[T] vs Vec.

1. Обработка текстовых данных (NLP-задачи)

  • Токенизация и препроцессинг текста
    Входной текст часто приходит как &str. Токенизатор может возвращать Vec<Cow<str>>: если токен — это подстрока оригинального текста (без изменений), используется Borrowed, если требуется модификация (lowercase, stemming, добавление special tokens) — только тогда создаётся Owned(String).
    Примеры: rust-bert, huggingface tokenizers (в некоторых реализациях), кастомные токенизаторы.
  • Нормализация и очистка строк
    Очистка от стоп-слов, HTML-тегов, пунктуации — Cow позволяет делать модификации лениво, копируя только те строки, которые действительно изменились.
  • Детокенизация
    При генерации текста из токенов часто можно ссылаться на оригинальные подстроки, избегая лишних аллокаций.

2. API моделей и inference

  • Приём входных данных
    Функции inference принимают Cow<str> (или Cow<[u8]> для байтовых данных), позволяя вызывающему коду передавать как &str, так и String без лишних копий.
    Это удобно в веб-сервисах (Axum, Actix), где запросы приходят в разных формах.
  • Возврат результатов
    Модель может возвращать Cow<str> для сгенерированного текста или предсказанных лейблов — borrowed, если данные взяты из статического словаря, owned — если сгенерированы динамически.

3. Работа с датасетами и фичами

  • Хранение названий фич и меток классовVec<Cow<str>> или HashMap<Cow<str>, ...> для feature names / class labels. Если названия известны статически — Borrowed('static), если загружаются из файла/генерируются — Owned.
  • Векторы признаковCow<[f32]> или Cow<[f64]> для батчей или отдельных сэмплов. Позволяет принимать как borrowed slice из буфера, так и owned Vec, и копировать только при необходимости (например, при нормализации/аугментации).
  • DataLoader и итераторы по датасету
    При чтении из CSV/Parquet/mmap-файлов строки или массивы можно держать как borrowed, а копировать только при трансформациях (shuffle, augmentation).

4. Конфигурации и метаданные моделей

  • Пути к файлам моделей/чекпоинтамCow<Path> — удобно принимать как &Path, так и PathBuf, особенно в CLI-инструментах и конфигах.
  • Гиперпараметры и конфиги
    Строковые параметры (названия optimizer’ов, scheduler’ов) как Cow<'static, str> — статические значения без аллокаций, динамические — с копированием.
  • Serde (сериализация/десериализация)
    В структурах моделей/датасетов поля типа Cow<str> позволяют десериализовать как borrowed (если данные в буфере), так и owned, и сериализовать эффективно. Serde имеет встроенную поддержку Cow.

5. Словарь (vocabulary) и эмбеддинги

  • Vocab в NLP-моделяхHashMap<Cow<str>, usize> или BTreeMap<Cow<str>, ...> — ключи могут быть borrowed из обучающего корпуса, если не требуется владение.
  • Embedding lookup
    При построении embedding-таблиц можно временно использовать Cow, чтобы избежать копий при инициализации из предобученных весов.

6. Прочие нишевые случаи

  • Логирование и метрики
    Названия метрик, теги экспериментов как Cow<str> — часто статические строки.
  • Кастомные трансформации в пайплайнах
    В библиотеках вроде linfa, polars-ml или кастомных пайплайнах — Cow помогает писать «универсальные» трансформации, которые работают и с borrowed, и с owned данными без дублирования кода.
  • Работа с байтовыми данными изображений/аудиоCow<[u8]> для raw-данных — borrowed из файла/mmap, owned после предобработки (resize, normalization).

Пример использования Cow в токенизаторе

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

use std::borrow::Cow;

fn tokenize_and_normalize(text: &str) -> Vec<Cow<str>> {

text

.split_whitespace() // простая токенизация по пробелам

.map(|token| {

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

if token.chars().any(char::is_uppercase) {

Cow::Owned(token.to_lowercase())

} else {

// Если изменений не нужно — просто ссылаемся на оригинальную подстроку

Cow::Borrowed(token)

}

})

.collect()

}

fn main() {

let text = "Hello World, Rust is GREAT for ML!";

let tokens = tokenize_and_normalize(text);

for token in &tokens {

println!("{:?}", token);

}

// Вывод:

// "hello" ← Owned (была копия, т.к. были заглавные)

// "World," ← Owned (была копия)

// "rust" ← Owned

// "is" ← Borrowed (без изменений)

// "great" ← Owned

// "for" ← Borrowed

// "ML!" ← Owned

}

Почему это работает эффективно

  • split_whitespace() возвращает итератор по &str — подстрокам оригинального text. Они живут столько же, сколько text.
  • Cow::Borrowed(token) — просто ссылка, без аллокаций.
  • Cow::Owned(token.to_lowercase()) — аллокация и копия происходят только для токенов, где есть заглавные буквы.
  • Результат Vec<Cow<str>> можно дальше передавать в модель (например, в embedding lookup или transformer), и при необходимости вызвать .to_mut() или .into_owned() для принудительного владения.