Параплан над живописным ландшафтом с озером и горами под голубым небом.

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

Введение в нейронный машинный перевод

Делиться

9cf105101678f812109b3f731977dd6b

Вследствие бума искусственного интеллекта темпы технологического совершенствования достигли беспрецедентного уровня. Прежние препятствия, похоже, теперь имеют жизнеспособные решения. Эта статья служит своего рода руководством «НМТ 101». Представляя наш проект, она также шаг за шагом знакомит читателей с процессом тонкой настройки существующей модели перевода для поддержки языка с ограниченными ресурсами, который не включен в основные многоязычные модели.

Введение: язык дунсян как язык с ограниченными ресурсами

Дунсян — это язык меньшинства, на котором говорят в китайской провинции Ганьсу, и который классифицируется ЮНЕСКО как уязвимый в Атласе языков мира, находящихся под угрозой исчезновения. Несмотря на широкое распространение в местных сообществах, языку дунсян не хватает институциональной и цифровой поддержки, которой обладают языки с богатыми ресурсами. Прежде чем приступить к обучению, полезно кратко ознакомиться с самим языком. Дунсян, как следует из названия, является родным языком народа дунсян. Происходя от центральноазиатских групп, мигрировавших в Ганьсу во времена династии Юань, сообщество дунсян имеет языковые корни, тесно связанные со среднемонгольским языком. С точки зрения письменности, стандартизация языка дунсян произошла сравнительно недавно. С 1990-х годов, благодаря государственной поддержке, язык постепенно принял официальную орфографию на основе латыни, используя 26 букв английского алфавита и разделяя слова пробелами.

8dc409ed6f7b23abc12edb1c89769bda

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

bcb3eed51e687d3e5284bba23ce06bc2

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

Наша модель: как использовать систему перевода

Наша система перевода построена на основе NLLB-200-distilled-600M, многоязычной нейронной модели машинного перевода, выпущенной Meta в рамках проекта No Language Left Behind (NLLB). Мы вдохновились работой Дэвида Дейла. Однако постоянные обновления библиотеки Transformers затруднили применение первоначального подхода. В наших собственных экспериментах откат к более ранним версиям (например, transformers ≤ 4.33) часто вызывал конфликты с другими зависимостями. В свете этих ограничений мы предоставляем полный список библиотек в файле requirements.txt на GitHub нашего проекта для вашего удобства.

345be8f860eb79e803fee73783c2960b

Наша модель была доработана на 42 868 парах двуязычных предложений на китайском языке (дунсян-китайский). Корпус для обучения сочетает в себе общедоступные материалы и внутренние ресурсы, предоставленные местными правительственными партнерами, которые были предварительно обработаны и очищены. Обучение проводилось с использованием Adafactor, эффективного с точки зрения памяти оптимизатора, хорошо подходящего для больших моделей трансформеров. Благодаря оптимизированной архитектуре, полный процесс доработки может быть завершен менее чем за 12 часов на одном графическом процессоре NVIDIA A100. Все конфигурации обучения, гиперпараметры и экспериментальные настройки задокументированы в двух обучающих блокнотах Jupyter. Вместо того чтобы полагаться на одну двунаправленную модель, мы обучили две модели, специфичные для каждого направления, для поддержки перевода с китайского на китайский и с китайского на дунсян. Поскольку NLLB уже предварительно обучена на китайском языке, совместное обучение в условиях несбалансированных данных, как правило, отдает предпочтение более легкому или более доминирующему направлению. В результате, прирост производительности на стороне с ограниченными ресурсами (дунсян) часто ограничен. Однако NLLB поддерживает двунаправленный перевод в рамках одной модели, и простой подход заключается в чередовании направлений перевода на уровне пакета.

Здесь вы найдете ссылки на наш репозиторий и веб-сайт.

Репозиторий GitHub
Веб-сайт, размещенный на GitHub.

Эта модель также находится в открытом доступе на сайте Hugging Face.

Китайский → Дунсян
Дунсян → Китайский

Обучение модели: пошаговый воспроизводимый конвейер

Прежде чем следовать этому алгоритму построения модели, мы предполагаем, что читатель обладает базовым пониманием Python и основных концепций обработки естественного языка. Для читателей, которые могут быть менее знакомы с этими темами, курсы Эндрю Нга являются настоятельно рекомендуемым отправным пунктом. Лично я также начал свой путь в этой области благодаря его курсу.

Шаг 1: Обработка двуязычного набора данных

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

import re import pandas as pd def split_lines(s: str): if «\n» in s and «n» not in s: lines = s.split(«\n») else: lines = s.splitlines() lines = [ln.strip().strip(«'»).strip() for ln in lines if ln.strip()] return lines def clean_dxg(s: str) -> str: s = re.sub(r»[^A-Za-zs,.?]», » «, s) s = re.sub(r»s+», » «, s).strip() s = re.sub(r»[,.?]+$», «», s) return s def clean_zh(s: str) -> str: s = re.sub(r»[^u4e00-u9fff,。?]», «», s) s = re.sub(r»[,。?]+$», «», s) return s def make_pairs(raw: str) -> pd.DataFrame: lines = split_lines(raw) pairs = [] for i in range(0, len(lines) — 1, 2): dxg = clean_dxg(lines[i]) zh = clean_zh(lines[i+1]) if dxg or zh: pairs.append({«Dongxiang»: dxg, «Chinese»: zh}) return pd.DataFrame(pairs, columns=[«Dongxiang», «Chinese»])

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

dfa2c94df77badc4ce0a9ef583f23d2f

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

Шаг 2: Подготовка токенизатора

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

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

d14caf42a2439057e79b743dbd6e681e

Вопрос о необходимости переобучения токенизатора лучше всего решается двумя критериями. Первый — это охват: частое появление неизвестных токенов () указывает на недостаточный словарный запас или обработку символов. В нашей выборке из 300 предложений на языке дунсян частота появления равна нулю, что свидетельствует о полном охвате при текущей предварительной обработке. Второй критерий — это фертильность субслов, определяемая как среднее количество токенов субслов, генерируемых на одно слово, разделенное пробелами. В 300 выборках предложения в среднем содержат 6,86 слов и 13,48 токенов, что соответствует фертильности приблизительно 1,97. Эта закономерность сохраняется по всему распределению, и нет никаких признаков чрезмерной фрагментации в более длинных предложениях.

В целом, NLLB демонстрирует устойчивое поведение даже на ранее не встречавшихся языках. В результате переобучение токенизатора, как правило, не требуется, если только целевой язык не использует крайне нетрадиционную систему письма или даже не имеет поддержки Unicode. Переобучение токенизатора SentencePiece также имеет значение для слоя встраивания. Новые токены начинают работу без предварительно обученных встраиваний и должны быть инициализированы с использованием случайных значений или простого усреднения.

Шаг 3: Регистрация языкового идентификатора

В практических системах машинного перевода, таких как Google Translate, исходный и целевой языки должны быть явно указаны. NLLB придерживается того же предположения. Перевод регулируется явно заданными языковыми тегами, обозначаемыми как src_lang и tgt_lang, которые определяют, как текст кодируется и генерируется в модели. Если язык выходит за рамки предопределенной области NLLB, его необходимо сначала явно зарегистрировать, а также выполнить соответствующее расширение слоя встраивания модели. Слой встраивания преобразует дискретные токены в непрерывные векторные представления, позволяя нейронной сети обрабатывать и изучать лингвистические шаблоны в числовой форме.

В нашей реализации к токенизатору добавляется пользовательский языковой тег в качестве дополнительного специального токена, который присваивает ему уникальный идентификатор. Затем матрица векторного представления токенов модели изменяется в размере, чтобы вместить расширенный словарь. Вектор векторного представления, связанный с новым языковым тегом, инициализируется из нормального распределения с центром в нуле и небольшой дисперсией, масштабированной на 0,02. Если вновь введенный язык тесно связан с уже существующим поддерживаемым языком, его векторное представление часто может быть обучено на основе существующего пространства представлений. Однако одного лишь лингвистического сходства недостаточно для эффективного переноса обучения. Различия в системах письма могут влиять на токенизацию. Хорошо известный пример — молдавский язык, который лингвистически идентичен румынскому, но пишется латинским алфавитом, в то время как в так называемой Приднестровской Молдавской Республике он пишется кириллицей. Несмотря на тесную лингвистическую связь, разница в алфавите приводит к различным моделям токенизации.

Здесь представлен код, используемый для регистрации нового языка.

def fix_tokenizer(tokenizer, new_lang: str): old = list(tokenizer.additional_special_tokens) if new_lang not in old: tokenizer.add_special_tokens( {«additional_special_tokens»: old + [new_lang]}) return tokenizer.convert_tokens_to_ids(new_lang) fix_tokenizer(tokenizer,»sce_Latn») # мы регистрируем Dongxiang как sce_Latn, и он должен быть добавлен к последнему # вывод 256204 print(tokenizer.convert_ids_to_tokens([256100,256204])) print(tokenizer.convert_tokens_to_ids(['lao_Laoo','sce_Latn'])) # вывод ['lao_Laoo', 'sce_Latn'] [256100, 256204] model = AutoModelForSeq2SeqLM.from_pretrained(«facebook/nllb-200-distilled-600M») model.resize_token_embeddings(len(tokenizer)) new_id = fix_tokenizer(tokenizer, «sce_Latn») embed_dim = model.model.shared.weight.size(1) model.model.shared.weight.data[new_id] = torch.randn(embed_dim) * 0.02

Шаг 4: Обучение модели

Мы доработали модель перевода, используя оптимизатор Adafactor — алгоритм оптимизации с высокой эффективностью использования памяти, разработанный для крупномасштабных моделей преобразования последовательностей. Обучение начинается с 500 шагов разминки, в течение которых скорость обучения постепенно увеличивается до 1e-4 для стабилизации ранней оптимизации и предотвращения резких скачков градиента. Затем модель обучается в течение 8000 шагов оптимизации, по 64 пары предложений на каждом шаге оптимизации (пакет). Максимальная длина последовательности установлена на уровне 128 токенов, а обрезка градиента применяется с порогом 1,0.

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

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

optimizer = Adafactor( [p for p in model.parameters() if p.requires_grad], scale_parameter=False, relative_step=False, lr=1e-4, clip_threshold=1.0, weight_decay=1e-3, ) batch_size = 64 max_length = 128 training_steps = 8000 warmup_steps = 500

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

loss = model(**x, labels=y.input_ids).loss # Псевдокод ниже иллюстрирует основной механизм функции потерь для каждого пакета: x = tokenize(source_sentences) # вход: токены исходного языка y = tokenize(target_sentences) # цель: токены эталонного перевода predictions = model.forward(x) # прогнозирование распределения следующих токенов loss = cross_entropy(predictions, y) # сравнение с эталонными токенами backpropagate(loss) update_model_parameters()

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

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

Шаг 5: Оценка модели

После построения модели следующим шагом является оценка качества перевода. Качество перевода зависит не только от самой модели, но и от того, как процесс перевода настроен во время вывода. В рамках NLLB целевой язык должен быть явно указан во время генерации. Это делается с помощью параметра forced_bos_token_id, который привязывает выходные данные к предполагаемому языку. Длина выходных данных контролируется двумя параметрами. Первый — это минимально допустимое количество выходных данных (a), которое гарантирует базовое количество токенов, которые модель может сгенерировать. Второй — это масштабный коэффициент (b), который определяет, как максимальная длина выходных данных увеличивается пропорционально длине входных данных. Максимальное количество сгенерированных токенов устанавливается как линейная функция длины входных данных и вычисляется как a + b × input_length. Кроме того, max_input_length ограничивает количество входных токенов, которые считывает модель.

Эта функция обеспечивает перевод с языка дунсян на китайский.

import torch from transformers import AutoModelForSeq2SeqLM, AutoTokenizer device = «cuda» if torch.cuda.is_available() else «cpu» MODEL_DIR3 = «/content/drive/MyDrive/my_nllb_CD_model» tokenizer3 = AutoTokenizer.from_pretrained(MODEL_DIR3) model3 = AutoModelForSeq2SeqLM.from_pretrained(MODEL_DIR3).to(device) model3.eval() def translate3(text, src_lang=»zho_Hans», tgt_lang=»sce_Latn», a=16, b=1.5, max_input_length=1024, **kwargs): tokenizer3.src_lang = src_lang inputs = tokenizer3(text, return_tensors=»pt», padding=True, truncation=True, max_length=max_input_length).to(model3.device) result = model3.generate( **inputs, forced_bos_token_id=tokenizer3.convert_tokens_to_ids(tgt_lang), max_new_tokens=int(a + b * inputs.input_ids.shape[1]), **kwargs ) outputs = tokenizer3.batch_decode(result, skip_special_tokens=True) return outputs

Качество модели затем оценивается с использованием комбинации автоматических метрик оценки и экспертной оценки. В количественном отношении мы приводим стандартные метрики машинного перевода, такие как BLEU и ChrF++. Показатели BLEU вычислялись с использованием стандартного BLEU-4, который измеряет перекрытие n-грамм на уровне слов от униграмм до четырехграмм и объединяет их с помощью геометрического среднего с учетом штрафа за краткость. Показатель ChrF++ рассчитывался по n-граммам на уровне символов и представлялся в виде F-меры. Следует отметить, что текущая оценка является предварительной. Из-за ограниченной доступности данных на этом раннем этапе показатели BLEU и ChrF++ были вычислены только на нескольких десятках отложенных пар предложений. Наша модель достигла следующих результатов:

Дунсян → Китайский (DX→ZH)
БЛЕУ-4: 44.00
ChrF++: 34.3

Китайский → Дунсян (ZH→DX)
БЛЕУ-4: 46.23
ChrF++: 59.80

Показатели BLEU-4 выше 40 обычно считаются высокими в условиях ограниченных ресурсов, что указывает на то, что модель с достаточной точностью улавливает структуру предложения и ключевые лексические особенности. Более низкий показатель chrF++ в направлении Дунсян → китайский язык ожидаем и не обязательно указывает на низкое качество перевода, поскольку китайский язык допускает существенные поверхностные вариации в выборе слов и структуре предложений, что уменьшает совпадение на уровне символов с одним эталонным переводом.

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

Шаг 6: Развертывание

На данном этапе мы развертываем проект с помощью облегченной конфигурации, размещая документацию и демонстрационный интерфейс на GitHub Pages, а обученные модели выпускаем на Hugging Face. Такой подход обеспечивает публичный доступ и взаимодействие с сообществом без дополнительных затрат на инфраструктуру. Подробная информация о развертывании на GitHub и размещении моделей Hugging Face приведена в официальной документации GitHub Pages и Hugging Face Hub соответственно.

Этот скрипт загружает локально обученную модель, совместимую с Hugging Face.

import os from huggingface_hub import HfApi, HfFolder # Загрузка токена доступа Hugging Face token = os.environ.get(«HF_TOKEN») HfFolder.save_token(token) # Путь к локальному каталогу, содержащему артефакты обученной модели local_dir = «/path/to/your/local_model_directory» # Целевой ID репозитория Hugging Face Hub в формате: имя пользователя/имя_репозитория repo_id = «ваше_имя_пользователя/имя_вашей_модели» # Загрузка всего каталога модели в Hugging Face Model Hub api = HfApi() api.upload_folder( folder_path=local_dir, repo_id=repo_id, repo_type=»model», )

После выпуска модели интерфейс на основе Gradio развертывается в виде «пространства объятий» и встраивается на сайт проекта на GitHub Pages. По сравнению с саморазвертыванием с использованием Docker, использование «пространств объятий» с Gradio позволяет избежать затрат на поддержание выделенной облачной инфраструктуры.

a9844e7983e2ed0c2ca71730185ddfbf

Отражение

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

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

8d43d2ed8b64f06ae597c95e39ca7c23

Контакт

Данная статья написана совместно Кайсуанем Ченом и Бо Ма, которые учились вместе на кафедре статистики в Университете Северной Каролины в Чапел-Хилле. Кайсуань Чен в настоящее время получает степень магистра в Северо-Западном университете, а Бо Ма — степень магистра в Калифорнийском университете в Сан-Диего. Оба автора открыты для профессиональных возможностей.

Если вас заинтересовала наша работа или вы хотели бы связаться с нами, пожалуйста, напишите нам:

Проект на GitHub: https://github.com/dongxiangtranslationproject
Кайсюань Чен: [email protected]
Бо Ма: [email protected]

Источник: towardsdatascience.com

✅ Найденные теги: Как, Машинный Перевод, Нейронная Система, новости, Ограниченные Ресурсы, язык

ОСТАВЬТЕ СВОЙ КОММЕНТАРИЙ

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

галерея

Человек спит в кровати под красным пледом, солнечный свет падает на подушку.
Человек в смокинге держит планеты Земля и Марс, символизируя космические достижения.
Твердотельный аккумулятор Donut на выставке, показывает замещающий литий-ион стоимость.
Человек рядом с изображением двойной спирали ДНК на фоне природы.
Залитый солнцем лес с деревьями и болотистой водой, покрытой зелёной растительностью.
Пленка NeoFilm 100 на деревянном столе в окружении упаковок.
Деревянный минималистичный сундук с подсветкой в интерьере.
Обложка отчета о преодолении разрыва в операционном ИИ от MIT Technology Review.
Твит о разработке в 2026: выполнение сложных задач до пробуждения США, чтобы избежать проблем с ИИ.
Image Not Found
Человек в смокинге держит планеты Земля и Марс, символизируя космические достижения.

Почему SpaceX может выйти на биржу и с чем это может быть связано

Мы ведь явно не воспринимаем всерьез центры обработки данных в космосе? Элизабет Лопатто, старший репортер. Публикации этого автора будут добавляться в вашу ежедневную рассылку по электронной почте и в ленту новостей на главной странице вашего сайта. Все…

Мар 5, 2026
Твердотельный аккумулятор Donut на выставке, показывает замещающий литий-ион стоимость.

Согласно результатам испытаний, твердотельная батарея Donut Lab способна выдерживать (экстремальные) температуры.

Разработанная финским стартапом батарея не только выдержала экстремальные условия высокой температуры, но и фактически увеличила свою емкость. Эндрю Дж. Хокинс, редактор раздела «Транспорт». Публикации этого автора будут добавляться в вашу ежедневную рассылку по электронной почте и в…

Мар 5, 2026
Пленка NeoFilm 100 на деревянном столе в окружении упаковок.

Цифровая камера OPT NeoFilm 100 в формате плёнки

Компактная камера OPT NeoFilm 100 выполнена в виде классической 35-мм плёнки, но внутри скрывается не аналоговый механизм, а цифровая «начинка», способная снимать фото и видео.  Камера оснащена 1-мегапиксельным сенсором, который позволяет получать изображения с разрешением до 3…

Мар 5, 2026
Деревянный минималистичный сундук с подсветкой в интерьере.

«Умная» кровать-трансформер Roll

Хорватский дизайнер Лука Булян разработал проект складной кровати Roll, которая по нажатию кнопки сворачивается в аккуратный деревянный шкаф. Главная идея строится на принципе ежедневного скручивания матраса без потери его свойств. Конструкция оснащена тихим электродвигателем и плавным механизмом…

Мар 5, 2026

Впишите свой почтовый адрес и мы будем присылать вам на почту самые свежие новости в числе самых первых