AI

Что такое MoE? Глубокое погружение в популярную архитектуру искусственного интеллекта

Откройте для себя возможности использования смеси экспертов в машинном обучении с помощью этого подробного руководства, в котором рассматриваются ее архитектура, преимущества и этапы внедрения.
9 мин. чтения
What is MoE blog image

В этом руководстве по “Смеси экспертов” вы узнаете:

  • Что такое МО и чем оно отличается от традиционных моделей
  • Преимущества использования
  • Пошаговое руководство по его реализации

Давайте погрузимся!

Что такое MoE?

MoE(Mixture of Experts) – это архитектура машинного обучения, которая объединяет несколько специализированных подмоделей – “экспертов” – в рамках более крупной системы. Каждый эксперт учится обрабатывать различные аспекты задачи или отдельные типы данных.

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

Архитектура МО

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

Различия между MoE и традиционными плотными моделями

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

Главное, что в плотных моделях все части задействованы для каждой задачи. Это контрастирует с MoE, которая активирует только соответствующие экспертные подразделы.

Ниже приведены основные различия между мо и плотными моделями:

  • Использование параметров:
    • Плотная модель: Для любого заданного входа модель использует все параметры в вычислениях.
    • Модель MoE: Для любого заданного входа модель использует только параметры выбранного эксперта(ов) и сети стробирования. Таким образом, если модель MoE имеет большое количество параметров, она задействует только часть из них для каждого отдельного вычисления.
  • Вычислительные затраты:
    • Плотная модель: Объем вычислений для плотного слоя фиксирован для каждого входа, так как все его части всегда задействованы.
    • Модель MoE: Вычислительные затраты на обработку входных данных через слой MoE могут быть ниже, чем у плотного слоя с сопоставимым общим размером параметров. Это происходит потому, что работу выполняет только подмножество модели – выбранные эксперты. Это позволяет моделям MoE масштабироваться до гораздо большего числа общих параметров без пропорционального увеличения вычислительных затрат на каждый отдельный вход.
  • Специализация и обучение:
    • Плотная модель: Все части плотного слоя учатся вносить свой вклад в обработку всех типов входных сигналов, с которыми они сталкиваются.
    • Модель MoE: Различные экспертные сети могут научиться специализироваться. Например, один эксперт может хорошо обрабатывать вопросы по истории, а другой специализируется на научных концепциях. Сеть стробирования учится определять тип входных данных и направлять их к наиболее подходящим экспертам. Это может привести к более тонкой и эффективной обработке.

Преимущества архитектуры Mixture of Experts

Архитектура MoE очень актуальна в современном ИИ, особенно при работе с LLM. Причина в том, что она предлагает способ увеличить емкость модели, то есть ее способность к обучению и хранению информации, без пропорционального увеличения вычислительных затрат в процессе использования.

К основным преимуществам МО в области ИИ относятся:

  • Сокращение времени ожидания вывода: Модели MoE позволяют сократить время, необходимое для создания прогноза или вывода, так называемое время ожидания вывода. Это происходит благодаря способности активировать только наиболее релевантных экспертов.
  • Повышенная масштабируемость и эффективность обучения: В процессе обучения ИИ можно использовать преимущества параллелизма в архитектурах MoE. Различные эксперты могут одновременно обучаться на разных подмножествах данных или специализированных задачах. Это может привести к ускорению сходимости и сокращению времени обучения.
  • Улучшенная модульность и ремонтопригодность моделей: Дискретная природа экспертных подсетей облегчает модульный подход к разработке и сопровождению модели. Отдельные эксперты могут быть независимо обновлены, переобучены или заменены улучшенными версиями, не требуя полного переобучения всей модели. Это упрощает интеграцию новых знаний или возможностей и позволяет осуществлять более целенаправленное вмешательство в случае ухудшения работы конкретного эксперта.
  • Потенциал повышения интерпретируемости: Специализация экспертов может дать более четкое представление о процессах принятия решений в модели. Анализ того, какие эксперты постоянно активируются при определенных входных данных, может дать представление о том, как модель научилась разделять пространство задач и определять их значимость. Эта характеристика дает возможность лучше понять сложное поведение модели по сравнению с монолитными плотными сетями.
  • Более высокая энергоэффективность в масштабе: Модели на основе MoE позволяют добиться более низкого энергопотребления на запрос по сравнению с традиционными плотными моделями. Это связано с редкой активацией параметров во время вывода, поскольку они используют только часть доступных параметров на вход.

Как внедрить МЭ: Пошаговое руководство

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

  1. sshleifer/distilbart-cnn-6-6: Резюмировать содержание каждой новости.
  2. distilbert-base-uncased-finetuned-sst-2-english: Вычисление настроения каждой новости. В анализе настроений “настроения” означают эмоциональный тон, мнение или отношение, выраженное в тексте. На выходе можно получить:
    • Позитивный: Выражает благоприятное мнение, счастье или удовлетворение.
    • Негативный: Выражает неблагоприятное мнение, печаль, гнев или неудовлетворенность.
    • Нейтральный: Не выражает сильных эмоций или мнений, часто является фактом.

В конце процесса каждая новость будет сохранена в JSON-файле, содержащем:

  • ID, заголовок и URL.
  • Краткое содержание.
  • Сентимент контента с оценкой доверия.

Набор данных, содержащий новости, можно получить с помощью API Web Scraper компании Bright Data – специализированных конечных точек для получения структурированных веб-данных из 100+ доменов в режиме реального времени.

Набор данных, содержащий входные JSON-данные, можно сгенерировать с помощью кода, приведенного в нашем руководстве “Понимание векторных баз данных: The Engine Behind Modern AI“. В частности, обратитесь к шагу 1 в главе “Практическая интеграция: Пошаговое руководство”.

Входной JSON-набор данных, называемый news-data.json, содержитмассив новостных статей, как показано ниже:

[
  {
    "id": "c787dk9923ro",
    "url": "https://www.bbc.com/sport/tennis/articles/c787dk9923ro",
    "author": "BBC",
    "headline": "Wimbledon plans to increase 'Henman Hill' capacity and accessibility",
    "topics": [
      "Tennis"
    ],
    "publication_date": "2025-04-03T11:28:36.326Z",
    "content": "Wimbledon is planning to renovate its iconic 'Henman Hill' and increase capacity for the tournament's 150th anniversary. Thousands of fans have watched action on a big screen from the grass slope which is open to supporters without show-court tickets. The proposed revamp - which has not yet been approved - would increase the hill's capacity by 20% in time for the 2027 event and increase accessibility. It is the latest change planned for the All England Club, after a 39-court expansion was approved last year. Advertisement "It's all about enhancing this whole area, obviously it's become extremely popular but accessibility is difficult for everyone," said four-time Wimbledon semi-finalist Tim Henman, after whom the hill was named. "We are always looking to enhance wherever we are on the estate. This is going to be an exciting project."",
    "videos": [],
    "images": [
      {
        "image_url": "https://ichef.bbci.co.uk/ace/branded_sport/1200/cpsprodpb/31f9/live/0f5b2090-106f-11f0-b72e-6314f702e779.jpg",
        "image_description": "Main image"
      },
      {
        "image_url": "https://ichef.bbci.co.uk/ace/standard/2560/cpsprodpb/31f9/live/0f5b2090-106f-11f0-b72e-6314f702e779.jpg",
        "image_description": "A render of planned improvements to Wimbledon's Henman Hill"
      }
    ],
    "related_articles": [
      {
        "article_title": "Live scores, results and order of playLive scores, results and order of play",
        "article_url": "https://www.bbc.com/sport/tennis/scores-and-schedule"
      },
      {
        "article_title": "Get tennis news sent straight to your phoneGet tennis news sent straight to your phone",
        "article_url": "https://www.bbc.com/sport/articles/cl5q9dk9jl3o"
      }
    ],
    "keyword": null,
    "timestamp": "2025-05-19T15:03:16.568Z",
    "input": {
      "url": "https://www.bbc.com/sport/tennis/articles/c787dk9923ro",
      "keyword": ""
    }
  },
  // omitted for brevity...
]

Следуйте инструкциям ниже и создайте свой пример MoE!

Необходимые условия и зависимости

Чтобы повторить этот урок, на вашей машине должен быть установлен Python 3.10.1 или выше.

Предположим, что вы назвали главную папку своего проекта moe_project/. По завершении этого шага папка будет иметь следующую структуру:

moe_project/
├── venv/
├── news-data.json
└── moe_analysis.py

Где:

  • venv/ содержит виртуальную среду Python.
  • news-data.json – это входной JSON-файл, содержащий новостные данные, которые вы соскребли с помощью Web Scraper API.
  • moe_analysis.py – это файл на языке Python, содержащий логику кодирования.

Вы можете создать каталог виртуальной среды venv/ следующим образом:

python -m venv venv

Чтобы активировать его, в Windows выполните команду:

venvScriptsactivate

Аналогично, в macOS и Linux выполните команду:

source venv/bin/activate

В активированной виртуальной среде установите зависимости с помощью:

pip install transformers torch

К этим библиотекам относятся:

  • transformers: библиотека Hugging Face для самых современных моделей машинного обучения.
  • torch: PyTorch, фреймворк машинного обучения с открытым исходным кодом.

Шаг №1: Установка и настройка

Инициализируйте файл moe_analysis.py, импортировав необходимые библиотеки и установив некоторые константы:

import json
from transformers import pipeline

# Define the input JSON file
JSON_FILE = "news-data.json"
# Specify the model for generating summaries
SUMMARIZATION_MODEL = "sshleifer/distilbart-cnn-6-6"
# Specify the model for analyzing sentiment
SENTIMENT_MODEL = "distilbert-base-uncased-finetuned-sst-2-english"

Этот код определяет:

  • Имя входного JSON-файла с отсканированными новостями.
  • Модели для экспертов.

Отлично! У вас есть все необходимое, чтобы начать работу с MoE на Python.

Шаг № 2: Определите эксперта по обобщению новостей

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

class NewsSummarizationLLMExpert:
    def __init__(self, model_name=SUMMARIZATION_MODEL):
        self.model_name = model_name
        self.summarizer = None

        # Initialize the summarization pipeline
        self.summarizer = pipeline(
            "summarization",
            model=self.model_name,
            tokenizer=self.model_name,
        )

    def analyze(self, article_content, article_headline=""):
        # Call the summarizer pipeline with the article content
        summary_outputs = self.summarizer(
            article_content,
            max_length=300,
            min_length=30,
            do_sample=False
        )
        # Extract the summary text from the pipeline's output
        summary = summary_outputs[0]["summary_text"]
        return { "summary": summary }

Приведенный выше код:

  • Инициализирует конвейер суммирования с помощью метода pipeline() из Hugging Face.
  • Определяет, как эксперт по подведению итогов должен обрабатывать статью с помощью метода analyze().

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

Шаг № 3: Определите эксперта по анализу настроений

По аналогии с экспертом по подведению итогов определите специализированный класс для проведения анализа настроений в новостях:

class SentimentAnalysisLLMExpert:
    def __init__(self, model_name=SENTIMENT_MODEL):
        self.model_name = model_name
        self.sentiment_analyzer = None 

        # Initialize the sentiment analysis pipeline
        self.sentiment_analyzer = pipeline(
            "sentiment-analysis",
            model=self.model_name,
            tokenizer=self.model_name,
        )

    def analyze(self, article_content, article_headline=""):
        # Define max tokens
        max_chars_for_sentiment = 2000
        # Truncate the content if it exceeds the maximum limit
        truncated_content = article_content[:max_chars_for_sentiment]
        # Call the sentiment analyzer pipeline
        sentiment_outputs = self.sentiment_analyzer(truncated_content)
        # Extract the sentiment label
        label = sentiment_outputs[0]["label"]
        # Extract the sentiment score
        score = sentiment_outputs[0]["score"]
        return { "sentiment_label": label, "sentiment_score": score }

Этот фрагмент:

  • Инициализирует конвейер анализа настроений с помощью метода pipeline().
  • Определяет метод analyze() для выполнения анализа настроения. Он также возвращает метку настроения – отрицательную или положительную – и оценку доверия.

Очень хорошо! Теперь у вас есть еще один эксперт, который вычисляет и выражает настроение текста в новостях.

Шаг № 4: Внедрение сети передачи данных

Теперь вам нужно определить логику работы сети шлюзов для маршрутизации экспертов:

def route_to_experts(item_data, experts_registry):
    chosen_experts = []
    # Select the summarizer and sentiment analyzer
    chosen_experts.append(experts_registry["summarizer"])
    chosen_experts.append(experts_registry["sentiment_analyzer"])
    return chosen_experts

В этой реализации сеть стробирования проста. Она всегда использует обоих экспертов для каждой новости, но делает это последовательно:

  1. В нем кратко изложен текст.
  2. Он рассчитывает настроение.

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

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

Фантастика! Логика сети стробирования настроена и готова к работе.

Шаг #5: Основная логика оркестровки для обработки новостных данных

Определите основную функцию, которая управляет всем рабочим процессом, определенным в следующем задании:

  1. Загрузите набор данных в формате JSON.
  2. Инициализируйте двух экспертов.
  3. Пройдитесь по всем новостям.
  4. Направьте их выбранным экспертам.
  5. Соберите результаты.

Это можно сделать с помощью следующего кода:

def process_news_json_with_moe(json_filepath):
    # Open and load news items from the JSON file
    with open(json_filepath, "r", encoding="utf-8") as f:
        news_items = json.load(f)

    # Create a dictionary to hold instances of expert classes
    experts_registry = {
        "summarizer": NewsSummarizationLLMExpert(),
        "sentiment_analyzer": SentimentAnalysisLLMExpert()
    }

    # List to store the analysis results
    all_results = []

    # Iterate through each news item in the loaded data
    for i, news_item in enumerate(news_items):
        print(f"n--- Processing Article {i+1}/{len(news_items)} ---")
        # Extract relevant data from the news item
        id = news_item.get("id")
        headline = news_item.get("headline")
        content = news_item.get("content")
        url = news_item.get("url")

        # Print progress
        print(f"ID: {id}, Headline: {headline[:70]}...")

        # Use the gating network to determine the expert to use
        active_experts = route_to_experts(news_item, experts_registry)

        # Prepare a dictionary to store the analysis results
        news_item_analysis_results = {
            "id": id,
            "headline": headline,
            "url": url,
            "analyses": {}
        }

        # Iterate through the experts and apply their analysis
        for expert_instance in active_experts:
            expert_name = expert_instance.__class__.__name__ # Get the class name of the expert
            try:
                # Call the expert's analyze method
                analysis_result = expert_instance.analyze(article_content=content, article_headline=headline)
                # Store the result under the expert's name
                news_item_analysis_results["analyses"][expert_name] = analysis_result

            except Exception as e:
                # Handle any errors during analysis by a specific expert
                print(f"Error during analysis with {expert_name}: {e}")
                news_item_analysis_results["analyses"][expert_name] = { "error": str(e) }

        # Add the current item's results to the overall list
        all_results.append(news_item_analysis_results)

    return all_results

В этом фрагменте:

  • Цикл for перебирает все загруженные новости.
  • Блок try-except выполняет анализ и устраняет ошибки, которые могут возникнуть. В данном случае ошибки, которые могут возникнуть, в основном связаны с параметрами max_length и max_chars_for_sentiment, определенными в предыдущих функциях. Поскольку не все найденное содержимое имеет одинаковую длину, управление ошибками является основой для эффективной обработки исключений.

Вот так! Вы определили функцию оркестровки всего процесса.

Шаг #6: Запуск функции обработки

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

# Call the main processing function with the input JSON file
final_analyses = process_news_json_with_moe(JSON_FILE)

print("nn--- MoE Analysis Complete ---")

# Write the final analysis results to a new JSON file
with open("analyzed_news_data.json", "w", encoding="utf-8") as f_out:
    json.dump(final_analyses, f_out, indent=4, ensure_ascii=False)

В приведенном выше коде:

  • Переменная final_analyses вызывает функцию для обработки данных с помощью MoE.
  • Проанализированные данные сохраняются в выходном файле analyzed_news_data.json.

И вуаля! Весь сценарий завершен, данные проанализированы и сохранены.

Шаг №7: Соберите все вместе и запустите код

Ниже показано, что теперь должен содержать файл moe_analysis.py:

import json
from transformers import pipeline

# Define the input JSON file
JSON_FILE = "news-data.json"
# Specify the model for generating summaries
SUMMARIZATION_MODEL = "sshleifer/distilbart-cnn-6-6"
# Specify the model for analyzing sentiment
SENTIMENT_MODEL = "distilbert-base-uncased-finetuned-sst-2-english"

# Define a class representing an expert for news summarization
class NewsSummarizationLLMExpert:
    def __init__(self, model_name=SUMMARIZATION_MODEL):
        self.model_name = model_name
        self.summarizer = None

        # Initialize the summarization pipeline
        self.summarizer = pipeline(
            "summarization",
            model=self.model_name,
            tokenizer=self.model_name,
        )

    def analyze(self, article_content, article_headline=""):
        # Call the summarizer pipeline with the article content
        summary_outputs = self.summarizer(
            article_content,
            max_length=300,
            min_length=30,
            do_sample=False
        )
        # Extract the summary text from the pipeline's output
        summary = summary_outputs[0]["summary_text"]
        return { "summary": summary }


# Define a class representing an expert for sentiment analysis
class SentimentAnalysisLLMExpert:
    def __init__(self, model_name=SENTIMENT_MODEL):
        self.model_name = model_name
        self.sentiment_analyzer = None

        # Initialize the sentiment analysis pipeline
        self.sentiment_analyzer = pipeline(
            "sentiment-analysis",
            model=self.model_name,
            tokenizer=self.model_name, 
        )


    def analyze(self, article_content, article_headline=""):
        # Define max tokens
        max_chars_for_sentiment = 2000
        # Truncate the content if it exceeds the maximum limit
        truncated_content = article_content[:max_chars_for_sentiment]
        # Call the sentiment analyzer pipeline
        sentiment_outputs = self.sentiment_analyzer(truncated_content)
        # Extract the sentiment label
        label = sentiment_outputs[0]["label"]
        # Extract the sentiment score
        score = sentiment_outputs[0]["score"]
        return { "sentiment_label": label, "sentiment_score": score }


# Define a gating network
def route_to_experts(item_data, experts_registry):
    chosen_experts = []
    # Select the summarizer and sentiment analyzer
    chosen_experts.append(experts_registry["summarizer"])
    chosen_experts.append(experts_registry["sentiment_analyzer"])
    return chosen_experts


# Main function to manage the orchestration process
def process_news_json_with_moe(json_filepath):
    # Open and load news items from the JSON file
    with open(json_filepath, "r", encoding="utf-8") as f:
        news_items = json.load(f)

    # Create a dictionary to hold instances of expert classes
    experts_registry = {
        "summarizer": NewsSummarizationLLMExpert(),
        "sentiment_analyzer": SentimentAnalysisLLMExpert()
    }

    # List to store the analysis results
    all_results = []

    # Iterate through each news item in the loaded data
    for i, news_item in enumerate(news_items):
        print(f"n--- Processing Article {i+1}/{len(news_items)} ---")
        # Extract relevant data from the news item
        id = news_item.get("id")
        headline = news_item.get("headline")
        content = news_item.get("content")
        url = news_item.get("url")

        # Print progress
        print(f"ID: {id}, Headline: {headline[:70]}...")

        # Use the gating network to determine the expert to use
        active_experts = route_to_experts(news_item, experts_registry)

        # Prepare a dictionary to store the analysis results
        news_item_analysis_results = {
            "id": id,
            "headline": headline,
            "url": url,
            "analyses": {}
        }

        # Iterate through the experts and apply their analysis
        for expert_instance in active_experts:
            expert_name = expert_instance.__class__.__name__ # Get the class name of the expert
            try:
                # Call the expert's analyze method
                analysis_result = expert_instance.analyze(article_content=content, article_headline=headline)
                # Store the result under the expert's name
                news_item_analysis_results["analyses"][expert_name] = analysis_result

            except Exception as e:
                # Handle any errors during analysis by a specific expert
                print(f"Error during analysis with {expert_name}: {e}")
                news_item_analysis_results["analyses"][expert_name] = { "error": str(e) }

        # Add the current item's results to the overall list
        all_results.append(news_item_analysis_results)

    return all_results

# Call the main processing function with the input JSON file
final_analyses = process_news_json_with_moe(JSON_FILE)

print("nn--- MoE Analysis Complete ---")

# Write the final analysis results to a new JSON file
with open("analyzed_news_data.json", "w", encoding="utf-8") as f_out:
    json.dump(final_analyses, f_out, indent=4, ensure_ascii=False)

Отлично! Вы только что завершили свой первый проект MoE, состоящий примерно из 130 строк кода.

Запустите код с помощью следующей команды:

python moe_analysis.py

Вывод в терминале должен содержать:

# Omitted for brevity...

--- Processing Article 6/10 ---
ID: cdrgdm4ye53o, Headline: Japanese Grand Prix: Lewis Hamilton says he has 'absolute 100% faith' ...

--- Processing Article 7/10 ---
ID: czed4jk7eeeo, Headline: F1 engines: A return to V10 or hybrid - what's the future?...
Error during analysis with NewsSummarizationLLMExpert: index out of range in self

--- Processing Article 8/10 ---
ID: cy700xne614o, Headline: Monte Carlo Masters: Novak Djokovic beaten as wait for 100th title con...
Error during analysis with NewsSummarizationLLMExpert: index out of range in self

# Omitted for brevity...

--- MoE Analysis Complete ---

По завершении выполнения в папке проекта появится выходной файл analyzed_news_data.json. Откройте его и сфокусируйтесь на одной из новостей. Поле "Анализ" будет содержать результаты сводного анализа и анализа настроения, выполненные двумя экспертами:

Результат применения подхода MoE в файле JSON

Как вы можете видеть, подход Министерства энергетики имеет свои особенности:

  • Резюмировал содержание статьи и представил ее в разделе резюме.
  • Определено положительное настроение с доверием 0,99.

Миссия выполнена!

Заключение

В этой статье вы узнали о MoE и о том, как реализовать его в реальном сценарии с помощью пошагового раздела.

Если вы хотите изучить больше сценариев MoE и для этого вам нужны свежие данные, Bright Data предлагает набор мощных инструментов и сервисов, предназначенных для получения обновленных данных с веб-страниц в режиме реального времени, преодолевая при этом препятствия, связанные со скраппингом.

Эти решения включают в себя:

  • Web Unlocker: API, позволяющий обойти защиту от скаппинга и получить чистый HTML с любой веб-страницы с минимальными усилиями.
  • Браузер для скрапинга: Облачный, управляемый браузер с рендерингом JavaScript. Он автоматически обрабатывает CAPTCHA, отпечатки пальцев браузера, повторные попытки и многое другое за вас.
  • API для веб-скреперов: Конечные точки для программного доступа к структурированным веб-данным с десятков популярных доменов.

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

Зарегистрируйтесь на сайте Bright Data прямо сейчас и начните бесплатную пробную версию, чтобы протестировать наши решения для сбора информации!