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

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

Введите Databricks, имя, которое является синонимом революционных достижений в области больших данных. В своем стремлении демократизировать ИИ они представили новаторскую программу: Большие языковые модели. Эти курсы, предназначенные как для начинающих энтузиастов, так и для опытных экспертов, предоставляют комплексную дорожную карту для овладения искусством создания и использования LLM для современных приложений. Давайте отправимся в путь открытий и углубимся в то, что делает эти курсы незаменимым активом для всех, кто увлечен миром машинного обучения и обработки естественного языка.

Программа LLM: краткий обзор

Программа Databricks Большие языковые модели состоит из двух отдельных, но взаимосвязанных курсов: LLM: применение через производство и LLM: базовые модели с нуля. Спектр тем, охватываемых этими курсами, настолько же разнообразен, насколько и всеобъемлющ.

Курс 1: LLM — применение через производство

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

Использование возможностей LLM. Вы научитесь применять LLM к реальным задачам НЛП, используя популярные библиотеки, такие как Hugging Face и LangChain. Благодаря практическим упражнениям вы научитесь использовать эти библиотеки для достижения максимального эффекта.

import torch
from transformers import AutoModelForCausalLM, AutoTokenizer

tokenizer = AutoTokenizer.from_pretrained("gpt2")
model = AutoModelForCausalLM.from_pretrained("gpt2")

# Generate text with GPT-2
input_text = "Once upon a time,"
input_ids = tokenizer(input_text, return_tensors="pt").input_ids
output = model.generate(input_ids, max_length=100)
generated_text = tokenizer.decode(output[0], skip_special_tokens=True)

print(generated_text)

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

from langchain.text_splitter import CharacterTextSplitter
from langchain.embeddings import HuggingFaceEmbeddings
from langchain.vectorstores import Chroma

# First we split the data into manageable chunks to store as vectors. 
# There isn't an exact way to do this, more chunks means more detailed context,
# but will increase the size of our vectorstore.
text_splitter = CharacterTextSplitter(chunk_size=250, chunk_overlap=10)
texts = text_splitter.split_documents(document)
# Now we'll create embeddings for our document so we can store it in a vector 
# store and feed the data into an LLM. We'll use the sentence-transformers 
# model for out embeddings. https://www.sbert.net/docs/pretrained_models.html#sentence-embedding-models/
model_name = "sentence-transformers/all-MiniLM-L6-v2"
embeddings = HuggingFaceEmbeddings(
    model_name=model_name, cache_folder=DA.paths.datasets
)  # Use a pre-cached model
# Finally we make our Index using chromadb and the embeddings LLM
chromadb_index = Chroma.from_documents(
    texts, embeddings, persist_directory=DA.paths.working_dir
)

from langchain.chains import RetrievalQA
from langchain.llms import HuggingFacePipeline

# We want to make this a retriever, so we need to convert our index.
# This will create a wrapper around the functionality of our vector database 
# so we can search for similar documents/chunks in the vectorstore and retrieve the results:
retriever = chromadb_index.as_retriever()

# This chain will be used to do QA on the document. We will need
# 1 - A LLM to do the language interpretation
# 2 - A vector database that can perform document retrieval
# 3 - Specification on how to deal with this data (more on this soon)

hf_llm = HuggingFacePipeline.from_model_id(
    model_id="google/flan-t5-large",
    task="text2text-generation",
    model_kwargs={
        "temperature": 0,
        "max_length": 128,
        "cache_dir": DA.paths.datasets,
    },
)

chain_type = "stuff"  # Options: stuff, map_reduce, refine, map_rerank
laptop_qa = RetrievalQA.from_chain_type(
    llm=hf_llm, chain_type="stuff", retriever=retriever
)

# Let's ask the chain about the product we have.
laptop_name = laptop_qa.run("What is the full name of the laptop?")
display(laptop_name)

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

import transformers as tr
training_args = tr.TrainingArguments(
    local_checkpoint_path,
    num_train_epochs=1,  # default number of epochs to train is 3
    per_device_train_batch_size=16,
    optim="adamw_torch",
    report_to=["tensorboard"],
)

# load the pre-trained model
model = tr.AutoModelForSeq2SeqLM.from_pretrained(
    model_checkpoint, cache_dir=DA.paths.datasets
)  # Use a pre-cached model

# used to assist the trainer in batching the data
data_collator = tr.DataCollatorWithPadding(tokenizer=tokenizer)
trainer = tr.Trainer(
    model,
    training_args,
    train_dataset=tokenized_dataset["train"],
    eval_dataset=tokenized_dataset["test"],
    tokenizer=tokenizer,
    data_collator=data_collator,
)

trainer.train()

# save model to the local checkpoint
trainer.save_model()
trainer.save_state()

# persist the fine-tuned model to DBFS
final_model_path = f"{DA.paths.working_dir}/llm_fine_tuning/{checkpoint_name}"
trainer.save_model(output_dir=final_model_path)

fine_tuned_model = tr.AutoModelForSeq2SeqLM.from_pretrained(final_model_path)

inputs = tokenizer(reviews, return_tensors="pt", truncation=True, padding=True)
pred = fine_tuned_model.generate(
    input_ids=inputs["input_ids"], attention_mask=inputs["attention_mask"]
)

Оценка эффективности и предвзятости. В эпоху, отмеченную этическими проблемами, научитесь строго оценивать эффективность и потенциальные предвзятости ваших программ LLM, используя различные методологии.

import evaluate
toxicity = evaluate.load("toxicity", module_type="measurement")

from transformers import AutoTokenizer, AutoModelForCausalLM
import shap

tokenizer = AutoTokenizer.from_pretrained(
    "gpt2", use_fast=True, cache_dir=DA.paths.datasets
)
model = AutoModelForCausalLM.from_pretrained("gpt2", cache_dir=DA.paths.datasets)

# Set model decoder to true
# GPT is a decoder-only model
model.config.is_decoder = True
# We set configurations for the output text generation
model.config.task_specific_params["text-generation"] = {
    "do_sample": True,
    "max_length": 50,
    "temperature": 0,  # to turn off randomness
    "top_k": 50,
    "no_repeat_ngram_size": 2,
}
input_sentence = ["Sunny days are the best days to go to the beach. So"]
explainer = shap.Explainer(model, tokenizer)
shap_values = explainer(input_sentence)
shap.plots.text(shap_values)
shap.plots.bar(shap_values[0, :, "looking"])

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

import mlflow

# Tell MLflow Tracking to user this explicit experiment path,
# which is in your home directory under the Workspace browser (left-hand sidebar).
mlflow.set_experiment(f"/Users/{DA.username}/LLM 06 - MLflow experiment")

with mlflow.start_run():
    # LOG PARAMS
    mlflow.log_params(
        {
            "hf_model_name": hf_model_name,
            "min_length": min_length,
            "max_length": max_length,
            "truncation": truncation,
            "do_sample": do_sample,
        }
    )

# It is valuable to log a "signature" with the model telling MLflow the input and output schema for the model.
  signature = mlflow.models.infer_signature(
      xsum_sample["document"][0],
      mlflow.transformers.generate_signature_output(
          summarizer, xsum_sample["document"][0]
      ),
  )
  print(f"Signature:\n{signature}\n")

  # For mlflow.transformers, if there are inference-time configurations,
  # those need to be saved specially in the log_model call (below).
  # This ensures that the pipeline will use these same configurations when re-loaded.
  inference_config = {
      "min_length": min_length,
      "max_length": max_length,
      "truncation": truncation,
      "do_sample": do_sample,
  }

  # Logging a model returns a handle `model_info` to the model metadata in the tracking server.
  # This `model_info` will be useful later in the notebook to retrieve the logged model.
  model_info = mlflow.transformers.log_model(
      transformers_model=summarizer,
      artifact_path="summarizer",
      task="summarization",
      inference_config=inference_config,
      signature=signature,
      input_example="This is an example of a long news article which this pipeline can summarize for you.",
  )

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

Что мне понравилось в этом курсе:

  1. Практическая направленность. Этот курс предназначен для практических практиков. Он дает вам навыки, необходимые для создания приложений, ориентированных на LLM, с использованием популярных платформ, таких как Hugging Face и LangChain. Он идеально подходит для разработчиков, специалистов по обработке данных и инженеров, желающих применить LLM в реальных сценариях.
  2. Отраслевая экспертиза. Курс проводят лидеры отрасли и известные исследователи, в том числе профессор Стэнфорда Матей Захария и техническая группа, создавшая модель Dolly от Databricks. Их идеи и опыт открывают бесценные перспективы.
  3. Практические занятия. Включение практических занятий гарантирует, что вы не только поймете теорию, но и приобретете практический опыт. Вы сможете создавать свои собственные рабочие процессы LLM, готовые к производству.
  4. Этические соображения. В современном мире этические вопросы имеют первостепенное значение. В этом курсе рассматриваются социальные, безопасные и этические аспекты использования LLM, помогающие вам разработать ответственный подход к искусственному интеллекту.
  5. Лучшие практики LLMOps. Вы изучите лучшие практики LLMOps для развертывания моделей в большом масштабе. Это важно для тех, кто стремится интегрировать LLM в крупномасштабные приложения.

Что можно улучшить:

  1. Только для практиков. Хотя практическая направленность отлично подходит для практиков, она может не подходить для тех, кто ищет более глубокое теоретическое понимание LLM (это решается с помощью курса 2).
  2. Требуются предварительные знания. Чтобы получить максимальную пользу от курса, вам необходимо иметь практические знания в области машинного и глубокого обучения. Возможно, он не идеален для начинающих (некоторые предварительные условия см. в ресурсах ниже).

Курс 2: LLM — базовые модели с нуля

Для тех, кто жаждет более глубоких знаний, этот курс предлагает окунуться в самое сердце LLM. Идеально подходит для специалистов по данным и энтузиастов, заинтригованных внутренней работой базовых моделей, и охватывает следующее:

Понимание базовых моделей. Познакомьтесь с теорией и инновациями, которые проложили путь к базовым моделям, включая механизмы внимания, кодеры и декодеры, а также их эволюцию до GPT-4.

class TransformerEncoderBlock(nn.Module):
    def __init__(self, d_model, num_heads, conv_hidden_dim, dropout=0.1):
        super(TransformerEncoderBlock, self).__init__()
        self.attention = nn.MultiheadAttention(d_model, num_heads, dropout=dropout)
        self.norm1 = nn.LayerNorm(d_model)
        self.norm2 = nn.LayerNorm(d_model)
        self.feed_forward = FeedForward(d_model, conv_hidden_dim, dropout)
        self.dropout = nn.Dropout(dropout)

    def forward(self, x, mask=None):
        # Multi-Head Attention
        attn_output, _ = self.attention(x, x, x, attn_mask=mask)
        x = x + self.dropout(attn_output)
        x = self.norm1(x)

        # Feed Forward Network
        ff_output = self.feed_forward(x)
        x = x + self.dropout(ff_output)
        x = self.norm2(x)

        return x

class TransformerEncoder(nn.Module):
    def __init__(self, vocab_size, d_model, num_heads, conv_hidden_dim, num_layers, dropout=0.1):
        super(TransformerEncoder, self).__init__()
        self.word_embedding = nn.Embedding(vocab_size, d_model)
        self.position_embedding = nn.Embedding(1000, d_model)  # Assuming a maximum sequence length of 1000
        self.layers = nn.ModuleList(
            [
                TransformerEncoderBlock(d_model, num_heads, conv_hidden_dim, dropout)
                for _ in range(num_layers)
            ]
        )

    def forward(self, x, mask=None):
        seq_length = x.shape[1]
        positions = torch.arange(0, seq_length).expand(x.shape[0], seq_length).to(x.device)
        out = self.word_embedding(x) + self.position_embedding(positions)

        for layer in self.layers:
            out = layer(out, mask)

        return out

# Assume the following hyperparameters
vocab_size = 5000  # size of the vocabulary
d_model = 512  # dimension of the word embedding
num_heads = 8  # number of attention heads
conv_hidden_dim = 2048  # dimension of the hidden layer in the feed-forward network
num_layers = 6  # number of Transformer Encoder blocks
dropout = 0.1  # dropout rate

# Instantiate the model
model = TransformerEncoder(vocab_size, d_model, num_heads, conv_hidden_dim, num_layers, dropout)

# Generate some example input
input_tensor = torch.randint(0, vocab_size, (1, 20))  # batch size of 1 and sequence length of 20

# Forward pass through the model
output = model(input_tensor, mask=None)

print(f"The model has {sum(p.numel() for p in model.parameters() if p.requires_grad):,} trainable parameters")

Эффективная точная настройка. Изучите передовые методы трансферного обучения, такие как однократное обучение, обучение в несколько этапов и дистилляция знаний. Узнайте, как уменьшить размеры LLM, сохранив при этом производительность. Получите представление о текущих исследованиях и разработках, формирующих ландшафт LLM, от Flash Attention до методов LoRa, AliBi и PEFT.

import peft
from peft import LoraConfig, get_peft_model
from transformers import AutoModelForCausalLM, AutoTokenizer

model_name = "bigscience/bloomz-560m"
tokenizer = AutoTokenizer.from_pretrained(model_name)
foundation_model = AutoModelForCausalLM.from_pretrained(model_name)

lora_config = LoraConfig(
    r=<FILL_IN>,
    lora_alpha=1, # a scaling factor that adjusts the magnitude of the weight matrix. Usually set to 1
    target_modules=["query_key_value"],
    lora_dropout=0.05, 
    bias="none", # this specifies if the bias parameter should be trained. 
    task_type="CAUSAL_LM"
)

peft_model = get_peft_model(foundation_model, lora_config)
print(peft_model.print_trainable_parameters())

Рекомендации по развертыванию LLM: изучите квантование, чтобы ускорить работу моделей и использовать меньше памяти, преобразуя 32-битные числа с плавающей запятой в форматы с более низкой точностью, например 8-битные целые числа.

# Specify quantization configuration
net.qconfig = torch.ao.quantization.get_default_qconfig("onednn")

# Prepare the model for static quantization. This inserts observers in the model that will observe activation tensors during calibration.
net_prepared = torch.quantization.prepare(net)

# Now we convert the model to a quantized version.
net_quantized = torch.quantization.convert(net_prepared)

# Once the model is quantized, it can be used for inference in the same way 
# as the unquantized model, but it will use less memory and potentially have 
# faster inference times, at the cost of a possible decrease in accuracy.

Запуск LLM в массовом масштабе: узнайте, как создать нашу собственную упрощенную версию системы LLM, состоящей из нескольких экспертов (MoE), которая может обрабатывать широкий спектр шаблонов данных в различных областях знаний. модели их компонентов. Каждый эксперт имеет свой собственный набор параметров и обычно представляет собой более простую модель, чем необходимо для эффективного моделирования всего набора данных.

# Define the "hard gating" function
# This function decides which model to use based on the length of the input
def hard_gating_function(input):
    if len(input) < 10:
        # For inputs less than 10 characters long, use the GPT2 model
        return "gpt2", gpt2, gpt2_tokenizer
    elif len(input) < 100:
        # For inputs less than 100 characters long but greater than 10 characters, use the T5 model
        return "t5" , t5, t5_tokenizer
    else:
        # For inputs greater than 100 characters, use the BERT model
        return "bert", bert, bert_tokenizer

# Define the "soft gating" function
# This function assigns a weight to each model based on the length of the input, and all models are used to a certain extent to generate the output
def soft_gating_function(input):
    # The weights for each model are calculated using the softmax function, which outputs a probability distribution
    weights = F.softmax(torch.tensor([len(input), 100 - len(input), len(input)], dtype=torch.float), dim=0)
    # The weights for each model are returned along with the models and their tokenizers
    return {"gpt2": (gpt2, gpt2_tokenizer, weights[0]),
            "bert": (bert, bert_tokenizer, weights[1]),
            "t5": (t5, t5_tokenizer, weights[2])}

Будущее LLM за мультимодальностью: изучите внутреннюю работу Vision Transformers с помощью практического проекта кодирования для выполнения классификации видео с использованием X-CLIP для назначения вероятностей предоставленному текстовому описанию. Модель состоит из кодировщика текста, кодировщика межкадрового изображения, преобразователя многокадровой интеграции и генератора подсказок для конкретного видео.

from transformers import XCLIPProcessor, XCLIPModel

model_name = "microsoft/xclip-base-patch16-zero-shot"
processor = XCLIPProcessor.from_pretrained(model_name)
model = XCLIPModel.from_pretrained(model_name)

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

Что мне понравилось в этом курсе:

  1. Углубленные теоретические знания. Если вас интересует теория, лежащая в основе программ LLM, и вы хотите понять их внутреннюю работу, этот курс идеален. Он охватывает эволюцию базовых моделей, включая механизмы внимания, кодеры и декодеры, а также их вклад в такие модели, как GPT-4.
  2. Расширенное трансферное обучение. Вы изучите передовые методы трансферного обучения, такие как однократное обучение, обучение в несколько этапов и дистилляцию знаний, которые могут значительно уменьшить размер LLM при сохранении производительности.
  3. Ориентация на будущее: этот курс дает представление о последних разработках LLM, включая методы Flash Attention, LoRa, AliBi и PEFT. Это позволит вам быть в авангарде исследований LLM.
  4. Мультимодальные приложения. Понимание мультимодальных LLM становится все более важным, поскольку приложения искусственного интеллекта включают в себя текстовые, аудио и визуальные компоненты. Этот курс подготовит вас к таким испытаниям.

Что можно улучшить:

  1. Теоретический акцент. Хотя этот курс богат теоретическим содержанием, он может не предлагать столько практического опыта, сколько первый курс. Если вы предпочитаете более практичный подход, возможно, это не ваш лучший выбор.
  2. Рекомендуемые предварительные условия: хотя прохождение курса «LLM: применение через производство» рекомендуется, оно не является строго обязательным. Однако, если вы еще не прошли первый курс, вам, возможно, придется приложить дополнительные усилия, чтобы наверстать упущенное.

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

Распаковка опыта обучения

Курсы LLM Databricks — это больше, чем просто серия онлайн-лекций. Они предлагают захватывающий опыт обучения, обогащенный мудростью лидеров отрасли и известных исследователей. Несколько выдающихся особенностей, которые делают эти курсы замечательными:

Обучение под руководством эксперта

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

Практические лаборатории

Теория без практики – что корабль без компаса. Курсы Databricks дополнены практическими занятиями, которые позволяют вам применять новые знания в реальных сценариях. Эти лаборатории дают практические навыки, необходимые для создания собственных рабочих процессов LLM, готовых к производству.

Бесплатные материалы курса и сертификаты

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

Бесплатные ресурсы

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

Видео Лекции

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

Путь вперед: степень магистра права и не только

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

По данным IDC, к 2025 году 90% корпоративных приложений будут дополнены искусственным интеллектом, а спрос на специалистов в области LLM стремительно растет. По данным Burning Glass, за последние три года количество вакансий, требующих как НЛП, так и навыков глубокого обучения, выросло на 105%.

Потенциальные карьерные пути для выпускников курсов LLM Databricks столь же разнообразны, как и области применения самих LLM. От инженеров NLP/LLM до специалистов по данным, инженеров машинного обучения, разработчиков программного обеспечения и аналитиков-исследователей — возможности ограничены только вашим воображением.

Вердикт: стоит ли вам присоединяться к восстанию LLM?

В духе драматической барабанной дроби позвольте мне прояснить: курсы LLM от Databricks — это бесценный сундук с сокровищами для тех, кто серьезно относится к покорению мира больших языковых моделей. Знания, знания и опыт, которые вы получите, стоят своего веса в золотой латине (для всех вас, трекки).

Итак, стоит ли вам продолжать эти курсы? Без сомнения! Независимо от того, являетесь ли вы энтузиастом машинного обучения или специалистом по обработке данных, желающим повысить свой уровень, курсы LLM Databricks — это гипердвигатель, необходимый для путешествия к звездам.

Как сказал покойный великий Дуглас Адамс: «Не паникуйте» и записывайтесь на эти курсы. Да прибудет с вами LLM!

Вывод: присоединяйтесь к путешествию

Завершая эту одиссею по галактике больших языковых моделей, я призываю вас отправиться в собственное приключение. Курсы LLM Databricks — это не просто опыт обучения; это путешествие в будущее машинного обучения. Путешествуя по постоянно расширяющейся вселенной данных и языковых моделей, помните:

«Единственным ограничением нашей реализации завтрашнего дня будут наши сомнения сегодня». - Франклин Д. Рузвельт.

Запишитесь на курсы LLM Databricks, и пусть ваши сомнения исчезнут, как звезды на заре нового дня. Хлопайте 👏, подписывайтесь 🔔 и следите за обновлениями 📡, чтобы увидеть еще больше таких познавательных путешествий в мир технологий и данных. Космос знаний ждёт, и вместе мы смело пойдём туда, куда ещё никто не ступал.

Ресурсы

  1. Введение в курсы LLM Databricks
  2. LLM Сертификация
  3. Курс LLM 1: Применение через производство
  4. Курс LLM 2: Фундаментальные модели с нуля
  5. Курс LLM 1: Репозиторий GitHub, Слайды, Виделекции
  6. Курс LLM 2: Репозиторий GitHub, Слайды, видеолекции (скоро)
  7. Трансформеры-Уроки

Отказ от ответственности: я не получил никакой финансовой компенсации или услуг от Databricks, edX или какой-либо другой стороны за написание этой статьи. Мнения, высказанные здесь, полностью мои собственные и основаны на моем реальном опыте прохождения этих курсов LLM.

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