Merge pull request #499 from microsoft/update-translations

🌐 Update translations via Co-op Translator
This commit is contained in:
Lee Stott 2025-08-30 08:00:05 +01:00 committed by GitHub
commit 0117bb25ab
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
220 changed files with 201041 additions and 254 deletions

View File

@ -1,8 +1,8 @@
<!--
CO_OP_TRANSLATOR_METADATA:
{
"original_hash": "f3a6b0ddf7e6e3f33b2a543baf086dc9",
"translation_date": "2025-08-25T21:12:46+00:00",
"original_hash": "07191303b7ea2aff1d47e2b0fe4bb862",
"translation_date": "2025-08-29T22:49:02+00:00",
"source_file": "README.md",
"language_code": "bg"
}
@ -23,11 +23,22 @@ CO_OP_TRANSLATOR_METADATA:
# Изкуствен интелект за начинаещи - Учебна програма
|![ Скетч от [(@girlie_mac)](https://twitter.com/girlie_mac) ](./lessons/sketchnotes/ai-overview.png)|
|![Скица от @girlie_mac https://twitter.com/girlie_mac](../../translated_images/ai-overview.0857791951d19500d0ef8b803d77110c738dcafc52306e6d68724742cd4af167.bg.png)|
|:---:|
| Изкуствен интелект за начинаещи - _Скетч от [@girlie_mac](https://twitter.com/girlie_mac)_ |
| Изкуствен интелект за начинаещи - _Скица от [@girlie_mac](https://twitter.com/girlie_mac)_ |
Разгледайте света на **Изкуствения интелект** (AI) с нашата 12-седмична, 24-урочна учебна програма! Тя включва практически уроци, тестове и лабораторни упражнения. Програмата е подходяща за начинаещи и обхваща инструменти като TensorFlow и PyTorch, както и етика в AI.
Изследвайте света на **Изкуствения интелект** (AI) с нашата 12-седмична учебна програма, състояща се от 24 урока! Тя включва практически уроци, тестове и лабораторни упражнения. Програмата е подходяща за начинаещи и обхваща инструменти като TensorFlow и PyTorch, както и етика в AI.
### 🌐 Поддръжка на много езици
#### Поддържано чрез GitHub Action (Автоматизирано и винаги актуално)
[Френски](../fr/README.md) | [Испански](../es/README.md) | [Немски](../de/README.md) | [Руски](../ru/README.md) | [Арабски](../ar/README.md) | [Персийски (Фарси)](../fa/README.md) | [Урду](../ur/README.md) | [Китайски (опростен)](../zh/README.md) | [Китайски (традиционен, Макао)](../mo/README.md) | [Китайски (традиционен, Хонг Конг)](../hk/README.md) | [Китайски (традиционен, Тайван)](../tw/README.md) | [Японски](../ja/README.md) | [Корейски](../ko/README.md) | [Хинди](../hi/README.md) | [Бенгалски](../bn/README.md) | [Марати](../mr/README.md) | [Непалски](../ne/README.md) | [Пенджабски (Гурмукхи)](../pa/README.md) | [Португалски (Португалия)](../pt/README.md) | [Португалски (Бразилия)](../br/README.md) | [Италиански](../it/README.md) | [Полски](../pl/README.md) | [Турски](../tr/README.md) | [Гръцки](../el/README.md) | [Тайландски](../th/README.md) | [Шведски](../sv/README.md) | [Датски](../da/README.md) | [Норвежки](../no/README.md) | [Фински](../fi/README.md) | [Холандски](../nl/README.md) | [Иврит](../he/README.md) | [Виетнамски](../vi/README.md) | [Индонезийски](../id/README.md) | [Малайски](../ms/README.md) | [Тагалог (Филипински)](../tl/README.md) | [Суахили](../sw/README.md) | [Унгарски](../hu/README.md) | [Чешки](../cs/README.md) | [Словашки](../sk/README.md) | [Румънски](../ro/README.md) | [Български](./README.md) | [Сръбски (Кирилица)](../sr/README.md) | [Хърватски](../hr/README.md) | [Словенски](../sl/README.md) | [Украински](../uk/README.md) | [Бирмански (Мианмар)](../my/README.md)
**Ако желаете да добавите допълнителни езици, списъкът с поддържаните е наличен [тук](https://github.com/Azure/co-op-translator/blob/main/getting_started/supported-languages.md)**
## Присъединете се към общността
[![Azure AI Discord](https://dcbadge.limes.pink/api/server/kzRShWzttr)](https://discord.gg/kzRShWzttr)
## Какво ще научите
@ -44,82 +55,82 @@ CO_OP_TRANSLATOR_METADATA:
> [Намерете всички допълнителни ресурси за този курс в нашата колекция в Microsoft Learn](https://learn.microsoft.com/en-us/collections/7w28iy2xrqzdj0?WT.mc_id=academic-77998-bethanycheum)
* Бизнес случаи за използване на **AI в бизнеса**. Помислете за преминаване на [Въведение в AI за бизнес потребители](https://docs.microsoft.com/learn/paths/introduction-ai-for-business-users/?WT.mc_id=academic-77998-bethanycheum) в Microsoft Learn или [AI Business School](https://www.microsoft.com/ai/ai-business-school/?WT.mc_id=academic-77998-bethanycheum), разработен в сътрудничество с [INSEAD](https://www.insead.edu/).
* Бизнес случаи за използване на **AI в бизнеса**. Помислете за преминаване на [Въведение в AI за бизнес потребители](https://docs.microsoft.com/learn/paths/introduction-ai-for-business-users/?WT.mc_id=academic-77998-bethanycheum) или [AI Бизнес училище](https://www.microsoft.com/ai/ai-business-school/?WT.mc_id=academic-77998-bethanycheum), разработено в сътрудничество с [INSEAD](https://www.insead.edu/).
* **Класическо машинно обучение**, което е добре описано в нашата [Учебна програма за машинно обучение за начинаещи](http://github.com/Microsoft/ML-for-Beginners).
* Практически AI приложения, изградени с помощта на **[Когнитивни услуги](https://azure.microsoft.com/services/cognitive-services/?WT.mc_id=academic-77998-bethanycheum)**. За това препоръчваме да започнете с модулите на Microsoft Learn за [визия](https://docs.microsoft.com/learn/paths/create-computer-vision-solutions-azure-cognitive-services/?WT.mc_id=academic-77998-bethanycheum), [обработка на естествен език](https://docs.microsoft.com/learn/paths/explore-natural-language-processing/?WT.mc_id=academic-77998-bethanycheum), **[Генеративен AI с Azure OpenAI Service](https://learn.microsoft.com/en-us/training/paths/develop-ai-solutions-azure-openai/?WT.mc_id=academic-77998-bethanycheum)** и други.
* Специфични ML **облачни рамки**, като [Azure Machine Learning](https://azure.microsoft.com/services/machine-learning/?WT.mc_id=academic-77998-bethanycheum), [Microsoft Fabric](https://learn.microsoft.com/en-us/training/paths/get-started-fabric/?WT.mc_id=academic-77998-bethanycheum) или [Azure Databricks](https://docs.microsoft.com/learn/paths/data-engineer-azure-databricks?WT.mc_id=academic-77998-bethanycheum). Помислете за използване на [Изграждане и управление на решения за машинно обучение с Azure Machine Learning](https://docs.microsoft.com/learn/paths/build-ai-solutions-with-azure-ml-service/?WT.mc_id=academic-77998-bethanycheum) и [Изграждане и управление на решения за машинно обучение с Azure Databricks](https://docs.microsoft.com/learn/paths/build-operate-machine-learning-solutions-azure-databricks/?WT.mc_id=academic-77998-bethanycheum).
* **Разговорен AI** и **чатботове**. Има отделна учебна програма [Създаване на решения за разговорен AI](https://docs.microsoft.com/learn/paths/create-conversational-ai-solutions/?WT.mc_id=academic-77998-bethanycheum), а също така можете да се обърнете към [тази публикация в блога](https://soshnikov.com/azure/hello-bot-conversational-ai-on-microsoft-platform/) за повече подробности.
* **Разговорен AI** и **чатботове**. Има отделна [Създаване на решения за разговорен AI](https://docs.microsoft.com/learn/paths/create-conversational-ai-solutions/?WT.mc_id=academic-77998-bethanycheum) учебна пътека, а също така можете да се обърнете към [тази публикация в блога](https://soshnikov.com/azure/hello-bot-conversational-ai-on-microsoft-platform/) за повече подробности.
* **Дълбока математика** зад дълбокото обучение. За това бихме препоръчали [Deep Learning](https://www.amazon.com/Deep-Learning-Adaptive-Computation-Machine/dp/0262035618) от Ian Goodfellow, Yoshua Bengio и Aaron Courville, която също е достъпна онлайн на [https://www.deeplearningbook.org/](https://www.deeplearningbook.org/).
За леко въведение в темите за _AI в облака_ можете да разгледате [Започнете с изкуствен интелект в Azure](https://docs.microsoft.com/learn/paths/get-started-with-artificial-intelligence-on-azure/?WT.mc_id=academic-77998-bethanycheum) в Microsoft Learn.
За леко въведение в темите за _AI в облака_ можете да разгледате [Започнете с изкуствения интелект в Azure](https://docs.microsoft.com/learn/paths/get-started-with-artificial-intelligence-on-azure/?WT.mc_id=academic-77998-bethanycheum) учебна пътека.
# Съдържание
| | Връзка към урока | PyTorch/Keras/TensorFlow | Лаборатория |
| :-: | :------------------------------------------------------------------------------------------------------------------------------------------: | :---------------------------------------------------------------------------------------------: | ------------------------------------------------------------------------------ |
| 0 | [Настройка на курса](./lessons/0-course-setup/setup.md) | [Настройте вашата среда за разработка](./lessons/0-course-setup/how-to-run.md) | |
| 0 | [Настройка на курса](./lessons/0-course-setup/setup.md) | [Настройка на средата за разработка](./lessons/0-course-setup/how-to-run.md) | |
| I | [**Въведение в AI**](./lessons/1-Intro/README.md) | | |
| 01 | [Въведение и история на AI](./lessons/1-Intro/README.md) | - | - |
| II | **Символичен AI** |
| 02 | [Представяне на знания и експертни системи](./lessons/2-Symbolic/README.md) | [Експертни системи](https://github.com/microsoft/AI-For-Beginners/blob/main/lessons/2-Symbolic/Animals.ipynb) / [Онтология](https://github.com/microsoft/AI-For-Beginners/blob/main/lessons/2-Symbolic/FamilyOntology.ipynb) /[Граф на концепции](https://github.com/microsoft/AI-For-Beginners/blob/main/lessons/2-Symbolic/MSConceptGraph.ipynb) | |
| 02 | [Представяне на знания и експертни системи](./lessons/2-Symbolic/README.md) | [Експертни системи](./lessons/2-Symbolic/Animals.ipynb) / [Онтология](./lessons/2-Symbolic/FamilyOntology.ipynb) /[Граф на концепции](./lessons/2-Symbolic/MSConceptGraph.ipynb) | |
| III | [**Въведение в невронните мрежи**](./lessons/3-NeuralNetworks/README.md) |||
| 03 | [Перцептрон](./lessons/3-NeuralNetworks/03-Perceptron/README.md) | [Бележник](https://github.com/microsoft/AI-For-Beginners/blob/main/lessons/3-NeuralNetworks/03-Perceptron/Perceptron.ipynb) | [Лаборатория](./lessons/3-NeuralNetworks/03-Perceptron/lab/README.md) |
| 04 | [Многослойни перцептрони и създаване на собствена рамка](./lessons/3-NeuralNetworks/04-OwnFramework/README.md) | [Бележник](https://github.com/microsoft/AI-For-Beginners/blob/main/lessons/3-NeuralNetworks/04-OwnFramework/OwnFramework.ipynb) | [Лаборатория](./lessons/3-NeuralNetworks/04-OwnFramework/lab/README.md) |
| 05 | [Въведение в рамките (PyTorch/TensorFlow) и пренасищане](./lessons/3-NeuralNetworks/05-Frameworks/README.md) | [PyTorch](https://github.com/microsoft/AI-For-Beginners/blob/main/lessons/3-NeuralNetworks/05-Frameworks/IntroPyTorch.ipynb) / [Keras](https://github.com/microsoft/AI-For-Beginners/blob/main/lessons/3-NeuralNetworks/05-Frameworks/IntroKeras.ipynb) / [TensorFlow](https://github.com/microsoft/AI-For-Beginners/blob/main/lessons/3-NeuralNetworks/05-Frameworks/IntroKerasTF.ipynb) | [Лаборатория](./lessons/3-NeuralNetworks/05-Frameworks/lab/README.md) |
| IV | [**Компютърно зрение**](./lessons/4-ComputerVision/README.md) | [PyTorch](https://docs.microsoft.com/learn/modules/intro-computer-vision-pytorch/?WT.mc_id=academic-77998-cacaste) / [TensorFlow](https://docs.microsoft.com/learn/modules/intro-computer-vision-TensorFlow/?WT.mc_id=academic-77998-cacaste)| [Изследвайте компютърното зрение в Microsoft Azure](https://learn.microsoft.com/en-us/collections/7w28iy2xrqzdj0?WT.mc_id=academic-77998-bethanycheum) |
| 06 | [Въведение в компютърното зрение. OpenCV](./lessons/4-ComputerVision/06-IntroCV/README.md) | [Бележник](https://github.com/microsoft/AI-For-Beginners/blob/main/lessons/4-ComputerVision/06-IntroCV/OpenCV.ipynb) | [Лаборатория](./lessons/4-ComputerVision/06-IntroCV/lab/README.md) |
| 07 | [Конволюционни невронни мрежи](./lessons/4-ComputerVision/07-ConvNets/README.md) & [Архитектури на CNN](./lessons/4-ComputerVision/07-ConvNets/CNN_Architectures.md) | [PyTorch](https://github.com/microsoft/AI-For-Beginners/blob/main/lessons/4-ComputerVision/07-ConvNets/ConvNetsPyTorch.ipynb) /[TensorFlow](https://microsoft.github.io/AI-For-Beginners/lessons/4-ComputerVision/07-ConvNets/ConvNetsTF.ipynb) | [Лаборатория](./lessons/4-ComputerVision/07-ConvNets/lab/README.md) |
| 08 | [Предварително обучени мрежи и трансферно обучение](./lessons/4-ComputerVision/08-TransferLearning/README.md) и [Трикове за обучение](./lessons/4-ComputerVision/08-TransferLearning/TrainingTricks.md) | [PyTorch](https://github.com/microsoft/AI-For-Beginners/blob/main/lessons/4-ComputerVision/08-TransferLearning/TransferLearningPyTorch.ipynb) / [TensorFlow](https://github.com/microsoft/AI-For-Beginners/blob/main/lessons/3-NeuralNetworks/05-Frameworks/IntroKerasTF.ipynb) | [Лаборатория](./lessons/4-ComputerVision/08-TransferLearning/lab/README.md) |
| 09 | [Автокодери и VAEs](./lessons/4-ComputerVision/09-Autoencoders/README.md) | [PyTorch](https://github.com/microsoft/AI-For-Beginners/blob/main/lessons/4-ComputerVision/09-Autoencoders/AutoEncodersPyTorch.ipynb) / [TensorFlow](https://github.com/microsoft/AI-For-Beginners/blob/main/lessons/4-ComputerVision/09-Autoencoders/AutoencodersTF.ipynb) | |
| 10 | [Генеративни състезателни мрежи и трансфер на артистичен стил](./lessons/4-ComputerVision/10-GANs/README.md) | [PyTorch](https://github.com/microsoft/AI-For-Beginners/blob/main/lessons/4-ComputerVision/10-GANs/GANPyTorch.ipynb) / [TensorFlow](https://github.com/microsoft/AI-For-Beginners/blob/main/lessons/4-ComputerVision/10-GANs/GANTF.ipynb) | |
| 11 | [Откриване на обекти](./lessons/4-ComputerVision/11-ObjectDetection/README.md) | [TensorFlow](https://github.com/microsoft/AI-For-Beginners/blob/main/lessons/4-ComputerVision/11-ObjectDetection/ObjectDetection.ipynb) | [Лаборатория](./lessons/4-ComputerVision/11-ObjectDetection/lab/README.md) |
| 12 | [Семантично сегментиране. U-Net](./lessons/4-ComputerVision/12-Segmentation/README.md) | [PyTorch](https://github.com/microsoft/AI-For-Beginners/blob/main/lessons/4-ComputerVision/12-Segmentation/SemanticSegmentationPytorch.ipynb) / [TensorFlow](../../(https:/github.com/microsoft/AI-For-Beginners/blob/main/lessons/4-ComputerVision/12-Segmentation/SemanticSegmentationTF.ipynb)) | |
| V | [**Обработка на естествен език**](./lessons/5-NLP/README.md) | [PyTorch](https://docs.microsoft.com/learn/modules/intro-natural-language-processing-pytorch/?WT.mc_id=academic-77998-cacaste) /[TensorFlow](https://docs.microsoft.com/learn/modules/intro-natural-language-processing-TensorFlow/?WT.mc_id=academic-77998-cacaste) | [Изследвайте обработката на естествен език в Microsoft Azure](https://learn.microsoft.com/en-us/collections/7w28iy2xrqzdj0?WT.mc_id=academic-77998-bethanycheum)|
| 13 | [Представяне на текст. Bow/TF-IDF](./lessons/5-NLP/13-TextRep/README.md) | [PyTorch](https://github.com/microsoft/AI-For-Beginners/blob/main/lessons/5-NLP/13-TextRep/TextRepresentationPyTorch.ipynb) / [TensorFlow](https://github.com/microsoft/AI-For-Beginners/blob/main/lessons/5-NLP/13-TextRep/TextRepresentationTF.ipynb) | |
| 14 | [Семантични векторни представяния на думи. Word2Vec и GloVe](./lessons/5-NLP/14-Embeddings/README.md) | [PyTorch](https://github.com/microsoft/AI-For-Beginners/blob/main/lessons/5-NLP/14-Embeddings/EmbeddingsPyTorch.ipynb) / [TensorFlow](https://github.com/microsoft/AI-For-Beginners/blob/main/lessons/5-NLP/14-Embeddings/EmbeddingsTF.ipynb) | |
| 15 | [Моделиране на език. Обучение на собствени векторни представяния](./lessons/5-NLP/15-LanguageModeling/README.md) | [PyTorch](https://github.com/microsoft/AI-For-Beginners/blob/main/lessons/5-NLP/15-LanguageModeling/CBoW-PyTorch.ipynb) / [TensorFlow](https://github.com/microsoft/AI-For-Beginners/blob/main/lessons/5-NLP/15-LanguageModeling/CBoW-TF.ipynb) | [Лаборатория](./lessons/5-NLP/15-LanguageModeling/lab/README.md) |
| 16 | [Рекурентни невронни мрежи](./lessons/5-NLP/16-RNN/README.md) | [PyTorch](https://github.com/microsoft/AI-For-Beginners/blob/main/lessons/5-NLP/16-RNN/RNNPyTorch.ipynb) / [TensorFlow](https://github.com/microsoft/AI-For-Beginners/blob/main/lessons/5-NLP/16-RNN/RNNTF.ipynb) | |
| 17 | [Генеративни рекурентни мрежи](./lessons/5-NLP/17-GenerativeNetworks/README.md) | [PyTorch](https://microsoft.github.io/AI-For-Beginners/lessons/5-NLP/17-GenerativeNetworks/GenerativePyTorch.md) / [TensorFlow](https://microsoft.github.io/AI-For-Beginners/lessons/5-NLP/17-GenerativeNetworks/GenerativeTF.md) | [Лаборатория](./lessons/5-NLP/17-GenerativeNetworks/lab/README.md) |
| 18 | [Трансформъри. BERT.](./lessons/5-NLP/18-Transformers/READMEtransformers.md) | [PyTorch](https://github.com/microsoft/AI-For-Beginners/blob/main/lessons/5-NLP/18-Transformers/TransformersPyTorch.ipynb) /[TensorFlow](https://github.com/microsoft/AI-For-Beginners/blob/main/lessons/5-NLP/18-Transformers/TransformersTF.ipynb) | |
| 19 | [Разпознаване на именувани обекти](./lessons/5-NLP/19-NER/README.md) | [TensorFlow](https://microsoft.github.io/AI-For-Beginners/lessons/5-NLP/19-NER/NER-TF.ipynb) | [Лаборатория](./lessons/5-NLP/19-NER/lab/README.md) |
| 20 | [Големи езикови модели, програмиране с подсказки и задачи с малко примери](./lessons/5-NLP/20-LangModels/READMELargeLang.md) | [PyTorch](https://microsoft.github.io/AI-For-Beginners/lessons/5-NLP/20-LangModels/GPT-PyTorch.ipynb) | |
| 03 | [Перцептрон](./lessons/3-NeuralNetworks/03-Perceptron/README.md) | [Бележник](./lessons/3-NeuralNetworks/03-Perceptron/Perceptron.ipynb) | [Лаборатория](./lessons/3-NeuralNetworks/03-Perceptron/lab/README.md) |
| 04 | [Многослойни перцептрони и създаване на собствена рамка](./lessons/3-NeuralNetworks/04-OwnFramework/README.md) | [Бележник](./lessons/3-NeuralNetworks/04-OwnFramework/OwnFramework.ipynb) | [Лаборатория](./lessons/3-NeuralNetworks/04-OwnFramework/lab/README.md) |
| 05 | [Въведение във фреймуъркове (PyTorch/TensorFlow) и Overfitting](./lessons/3-NeuralNetworks/05-Frameworks/README.md) | [PyTorch](./lessons/3-NeuralNetworks/05-Frameworks/IntroPyTorch.ipynb) / [Keras](./lessons/3-NeuralNetworks/05-Frameworks/IntroKeras.ipynb) / [TensorFlow](./lessons/3-NeuralNetworks/05-Frameworks/IntroKerasTF.ipynb) | [Лабораторно упражнение](./lessons/3-NeuralNetworks/05-Frameworks/lab/README.md) |
| IV | [**Компютърно зрение**](./lessons/4-ComputerVision/README.md) | [PyTorch](https://docs.microsoft.com/learn/modules/intro-computer-vision-pytorch/?WT.mc_id=academic-77998-cacaste) / [TensorFlow](https://docs.microsoft.com/learn/modules/intro-computer-vision-TensorFlow/?WT.mc_id=academic-77998-cacaste)| [Изследване на компютърно зрение в Microsoft Azure](https://learn.microsoft.com/en-us/collections/7w28iy2xrqzdj0?WT.mc_id=academic-77998-bethanycheum) |
| 06 | [Въведение в компютърното зрение. OpenCV](./lessons/4-ComputerVision/06-IntroCV/README.md) | [Бележник](./lessons/4-ComputerVision/06-IntroCV/OpenCV.ipynb) | [Лабораторно упражнение](./lessons/4-ComputerVision/06-IntroCV/lab/README.md) |
| 07 | [Конволюционни невронни мрежи](./lessons/4-ComputerVision/07-ConvNets/README.md) & [Архитектури на CNN](./lessons/4-ComputerVision/07-ConvNets/CNN_Architectures.md) | [PyTorch](./lessons/4-ComputerVision/07-ConvNets/ConvNetsPyTorch.ipynb) /[TensorFlow](./lessons/4-ComputerVision/07-ConvNets/ConvNetsTF.ipynb) | [Лабораторно упражнение](./lessons/4-ComputerVision/07-ConvNets/lab/README.md) |
| 08 | [Предварително обучени мрежи и трансферно обучение](./lessons/4-ComputerVision/08-TransferLearning/README.md) и [Трикове за обучение](./lessons/4-ComputerVision/08-TransferLearning/TrainingTricks.md) | [PyTorch](./lessons/4-ComputerVision/08-TransferLearning/TransferLearningPyTorch.ipynb) / [TensorFlow](./lessons/3-NeuralNetworks/05-Frameworks/IntroKerasTF.ipynb) | [Лабораторно упражнение](./lessons/4-ComputerVision/08-TransferLearning/lab/README.md) |
| 09 | [Автоенкодери и VAEs](./lessons/4-ComputerVision/09-Autoencoders/README.md) | [PyTorch](./lessons/4-ComputerVision/09-Autoencoders/AutoEncodersPyTorch.ipynb) / [TensorFlow](./lessons/4-ComputerVision/09-Autoencoders/AutoencodersTF.ipynb) | |
| 10 | [Генеративни състезателни мрежи и трансфер на артистичен стил](./lessons/4-ComputerVision/10-GANs/README.md) | [PyTorch](./lessons/4-ComputerVision/10-GANs/GANPyTorch.ipynb) / [TensorFlow](./lessons/4-ComputerVision/10-GANs/GANTF.ipynb) | |
| 11 | [Откриване на обекти](./lessons/4-ComputerVision/11-ObjectDetection/README.md) | [TensorFlow](./lessons/4-ComputerVision/11-ObjectDetection/ObjectDetection.ipynb) | [Лабораторно упражнение](./lessons/4-ComputerVision/11-ObjectDetection/lab/README.md) |
| 12 | [Семантично сегментиране. U-Net](./lessons/4-ComputerVision/12-Segmentation/README.md) | [PyTorch](./lessons/4-ComputerVision/12-Segmentation/SemanticSegmentationPytorch.ipynb) / [TensorFlow](./lessons/4-ComputerVision/12-Segmentation/SemanticSegmentationTF.ipynb) | |
| V | [**Обработка на естествен език**](./lessons/5-NLP/README.md) | [PyTorch](https://docs.microsoft.com/learn/modules/intro-natural-language-processing-pytorch/?WT.mc_id=academic-77998-cacaste) /[TensorFlow](https://docs.microsoft.com/learn/modules/intro-natural-language-processing-TensorFlow/?WT.mc_id=academic-77998-cacaste) | [Изследване на обработката на естествен език в Microsoft Azure](https://learn.microsoft.com/en-us/collections/7w28iy2xrqzdj0?WT.mc_id=academic-77998-bethanycheum)|
| 13 | [Представяне на текст. Bow/TF-IDF](./lessons/5-NLP/13-TextRep/README.md) | [PyTorch](./lessons/5-NLP/13-TextRep/TextRepresentationPyTorch.ipynb) / [TensorFlow](./lessons/5-NLP/13-TextRep/TextRepresentationTF.ipynb) | |
| 14 | [Семантични векторни представяния. Word2Vec и GloVe](./lessons/5-NLP/14-Embeddings/README.md) | [PyTorch](./lessons/5-NLP/14-Embeddings/EmbeddingsPyTorch.ipynb) / [TensorFlow](./lessons/5-NLP/14-Embeddings/EmbeddingsTF.ipynb) | |
| 15 | [Моделиране на език. Обучение на собствени векторни представяния](./lessons/5-NLP/15-LanguageModeling/README.md) | [PyTorch](./lessons/5-NLP/15-LanguageModeling/CBoW-PyTorch.ipynb) / [TensorFlow](./lessons/5-NLP/15-LanguageModeling/CBoW-TF.ipynb) | [Лабораторно упражнение](./lessons/5-NLP/15-LanguageModeling/lab/README.md) |
| 16 | [Рекурентни невронни мрежи](./lessons/5-NLP/16-RNN/README.md) | [PyTorch](./lessons/5-NLP/16-RNN/RNNPyTorch.ipynb) / [TensorFlow](./lessons/5-NLP/16-RNN/RNNTF.ipynb) | |
| 17 | [Генеративни рекурентни мрежи](./lessons/5-NLP/17-GenerativeNetworks/README.md) | [PyTorch](./lessons/5-NLP/17-GenerativeNetworks/GenerativePyTorch.md) / [TensorFlow](./lessons/5-NLP/17-GenerativeNetworks/GenerativeTF.md) | [Лабораторно упражнение](./lessons/5-NLP/17-GenerativeNetworks/lab/README.md) |
| 18 | [Трансформъри. BERT.](./lessons/5-NLP/18-Transformers/READMEtransformers.md) | [PyTorch](./lessons/5-NLP/18-Transformers/TransformersPyTorch.ipynb) /[TensorFlow](./lessons/5-NLP/18-Transformers/TransformersTF.ipynb) | |
| 19 | [Разпознаване на именувани обекти](./lessons/5-NLP/19-NER/README.md) | [TensorFlow](./lessons/5-NLP/19-NER/NER-TF.ipynb) | [Лабораторно упражнение](./lessons/5-NLP/19-NER/lab/README.md) |
| 20 | [Големи езикови модели, програмиране с подсказки и задачи с малко примери](./lessons/5-NLP/20-LangModels/READMELargeLang.md) | [PyTorch](./lessons/5-NLP/20-LangModels/GPT-PyTorch.ipynb) | |
| VI | **Други AI техники** || |
| 21 | [Генетични алгоритми](./lessons/6-Other/21-GeneticAlgorithms/README.md) | [Бележник](https://github.com/microsoft/AI-For-Beginners/blob/main/lessons/6-Other/21-GeneticAlgorithms/Genetic.ipynb) | |
| 22 | [Дълбоко подсилващо обучение](./lessons/6-Other/22-DeepRL/README.md) | [PyTorch](https://github.com/microsoft/AI-For-Beginners/blob/main/lessons/6-Other/22-DeepRL/CartPole-RL-PyTorch.ipynb) /[TensorFlow](https://github.com/microsoft/AI-For-Beginners/blob/main/lessons/6-Other/22-DeepRL/CartPole-RL-TF.ipynb) | [Лаборатория](./lessons/6-Other/22-DeepRL/lab/README.md) |
| 23 | [Мултиагентни системи](./lessons/6-Other/23-MultiagentSystems/README.md) | | |
| 21 | [Генетични алгоритми](./lessons/6-Other/21-GeneticAlgorithms/README.md) | [Бележник](./lessons/6-Other/21-GeneticAlgorithms/Genetic.ipynb) | |
| 22 | [Дълбоко обучение чрез подсилване](./lessons/6-Other/22-DeepRL/README.md) | [PyTorch](./lessons/6-Other/22-DeepRL/CartPole-RL-PyTorch.ipynb) /[TensorFlow](./lessons/6-Other/22-DeepRL/CartPole-RL-TF.ipynb) | [Лабораторно упражнение](./lessons/6-Other/22-DeepRL/lab/README.md) |
| 23 | [Многоагентни системи](./lessons/6-Other/23-MultiagentSystems/README.md) | | |
| VII | **Етика в AI** | | |
| 24 | [Етика в AI и отговорен AI](./lessons/7-Ethics/README.md) | [Microsoft Learn: Принципи за отговорен AI](https://docs.microsoft.com/learn/paths/responsible-ai-business-principles/?WT.mc_id=academic-77998-cacaste) | |
| IX | **Допълнителни материали** | | |
| 25 | [Мултимодални мрежи, CLIP и VQGAN](./lessons/X-Extras/X1-MultiModal/README.md) | [Бележник](https://github.com/microsoft/AI-For-Beginners/blob/main/lessons/X-Extras/X1-MultiModal/Clip.ipynb) | |
| 25 | [Мултимодални мрежи, CLIP и VQGAN](./lessons/X-Extras/X1-MultiModal/README.md) | [Бележник](./lessons/X-Extras/X1-MultiModal/Clip.ipynb) | |
## Всяка лекция съдържа
* Материали за предварително четене
* Изпълними Jupyter бележници, които често са специфични за рамката (**PyTorch** или **TensorFlow**). Изпълнимият бележник също съдържа много теоретичен материал, така че за да разберете темата, трябва да преминете през поне една версия на бележника (или PyTorch, или TensorFlow).
* **Лаборатории**, налични за някои теми, които ви дават възможност да приложите наученото към конкретен проблем.
* Изпълними Jupyter бележници, които често са специфични за даден фреймуърк (**PyTorch** или **TensorFlow**). Изпълнимият бележник също съдържа много теоретичен материал, така че за да разберете темата, трябва да преминете поне през една версия на бележника (или PyTorch, или TensorFlow).
* **Лабораторни упражнения**, налични за някои теми, които ви дават възможност да приложите наученото към конкретен проблем.
* Някои секции съдържат връзки към модули в [**MS Learn**](https://learn.microsoft.com/en-us/collections/7w28iy2xrqzdj0?WT.mc_id=academic-77998-bethanycheum), които обхващат свързани теми.
## Начало
- Създадохме [урок за настройка](https://github.com/microsoft/AI-For-Beginners/blob/main/lessons/0-course-setup/setup.md), за да ви помогнем с настройката на вашата среда за разработка. - За преподаватели създадохме и [урок за настройка на учебна програма](https://github.com/microsoft/AI-For-Beginners/blob/main/lessons/0-course-setup/for-teachers.md)!
- Как да [стартирате кода във VSCode или Codepace](https://github.com/microsoft/AI-For-Beginners/blob/main/lessons/0-course-setup/how-to-run.md)
- Създадохме [лекция за настройка](./lessons/0-course-setup/setup.md), която да ви помогне с настройването на вашата среда за разработка. - За преподаватели създадохме и [лекция за настройка на учебната програма](./lessons/0-course-setup/for-teachers.md)!
- Как да [стартирате кода във VSCode или Codepace](./lessons/0-course-setup/how-to-run.md)
Следвайте тези стъпки:
Форкнете хранилището: Кликнете върху бутона "Fork" в горния десен ъгъл на тази страница.
Форкнете репозитория: Кликнете върху бутона "Fork" в горния десен ъгъл на тази страница.
Клонирайте хранилището: `git clone https://github.com/microsoft/AI-For-Beginners.git`
Клонирайте репозитория: `git clone https://github.com/microsoft/AI-For-Beginners.git`
Не забравяйте да добавите звезда (🌟) на това хранилище, за да го намерите по-лесно по-късно.
Не забравяйте да добавите звезда (🌟) на този репо, за да го намерите по-лесно по-късно.
## Срещнете други обучаващи се
Присъединете се към нашия [официален AI Discord сървър](https://aka.ms/genai-discord?WT.mc_id=academic-105485-bethanycheum), за да се срещнете и свържете с други обучаващи се, които преминават този курс, и да получите подкрепа.
Ако имате обратна връзка за продукта или въпроси по време на разработката, посетете нашия [Azure AI Foundry Developer Forum](https://aka.ms/foundry/forum)
Ако имате обратна връзка за продукта или въпроси по време на изграждането, посетете нашия [Azure AI Foundry Developer Forum](https://aka.ms/foundry/forum)
## Тестове
> **Бележка относно тестовете**: Всички тестове се намират в папката Quiz-app в etc\quiz-app. Те са свързани с уроците, като приложението за тестове може да се стартира локално или да се разположи в Azure; следвайте инструкциите в папката `quiz-app`. Те постепенно се локализират.
> **Бележка относно тестовете**: Всички тестове се намират в папката Quiz-app в etc\quiz-app, или [Онлайн тук](https://ff-quizzes.netlify.app/). Те са свързани с уроците, като приложението за тестове може да бъде стартирано локално или разположено в Azure; следвайте инструкциите в папката `quiz-app`. Те постепенно се локализират.
## Търсим помощ
Имате ли предложения или сте открили правописни или кодови грешки? Създайте проблем или направете pull request.
@ -151,5 +162,7 @@ CO_OP_TRANSLATOR_METADATA:
- [Mastering GitHub Copilot for C#/.NET Developers](https://github.com/microsoft/mastering-github-copilot-for-dotnet-csharp-developers)
- [Choose Your Own Copilot Adventure](https://github.com/microsoft/CopilotAdventures)
---
**Отказ от отговорност**:
Този документ е преведен с помощта на AI услуга за превод [Co-op Translator](https://github.com/Azure/co-op-translator). Въпреки че се стремим към точност, моля, имайте предвид, че автоматизираните преводи може да съдържат грешки или неточности. Оригиналният документ на неговия роден език трябва да се счита за авторитетен източник. За критична информация се препоръчва професионален човешки превод. Не носим отговорност за недоразумения или погрешни интерпретации, произтичащи от използването на този превод.
Този документ е преведен с помощта на AI услуга за превод [Co-op Translator](https://github.com/Azure/co-op-translator). Въпреки че се стремим към точност, моля, имайте предвид, че автоматичните преводи може да съдържат грешки или неточности. Оригиналният документ на неговия изходен език трябва да се счита за авторитетен източник. За критична информация се препоръчва професионален превод от човек. Ние не носим отговорност за каквито и да е недоразумения или погрешни интерпретации, произтичащи от използването на този превод.

View File

@ -0,0 +1,478 @@
{
"cells": [
{
"cell_type": "markdown",
"metadata": {
"collapsed": true
},
"source": [
"# Изграждане на експертна система за животни\n",
"\n",
"Пример от [AI for Beginners Curriculum](http://github.com/microsoft/ai-for-beginners).\n",
"\n",
"В този пример ще изградим проста система, базирана на знания, за определяне на животно въз основа на някои физически характеристики. Системата може да бъде представена чрез следното AND-OR дърво (това е част от цялото дърво, лесно можем да добавим още правила):\n",
"\n",
"![](../../../../translated_images/AND-OR-Tree.5592d2c70187f283703c8e9c0d69d6a786eb370f4ace67f9a7aae5ada3d260b0.bg.png)\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Наш собственен експертен системен шел с обратна логика\n",
"\n",
"Нека се опитаме да дефинираме прост език за представяне на знания, базиран на продукционни правила. Ще използваме Python класове като ключови думи за дефиниране на правила. Ще има основно три типа класове:\n",
"* `Ask` представлява въпрос, който трябва да бъде зададен на потребителя. Той съдържа набора от възможни отговори.\n",
"* `If` представлява правило и е просто синтактична захар за съхраняване на съдържанието на правилото.\n",
"* `AND`/`OR` са класове за представяне на AND/OR разклонения на дървото. Те просто съхраняват списъка с аргументи вътре. За опростяване на кода, цялата функционалност е дефинирана в родителския клас `Content`.\n"
]
},
{
"cell_type": "code",
"execution_count": 1,
"metadata": {
"trusted": true
},
"outputs": [],
"source": [
"class Ask():\n",
" def __init__(self,choices=['y','n']):\n",
" self.choices = choices\n",
" def ask(self):\n",
" if max([len(x) for x in self.choices])>1:\n",
" for i,x in enumerate(self.choices):\n",
" print(\"{0}. {1}\".format(i,x),flush=True)\n",
" x = int(input())\n",
" return self.choices[x]\n",
" else:\n",
" print(\"/\".join(self.choices),flush=True)\n",
" return input()\n",
"\n",
"class Content():\n",
" def __init__(self,x):\n",
" self.x=x\n",
" \n",
"class If(Content):\n",
" pass\n",
"\n",
"class AND(Content):\n",
" pass\n",
"\n",
"class OR(Content):\n",
" pass"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"В нашата система работната памет би съдържала списъка с **факти** като **атрибут-стойност двойки**. Базата знания може да бъде дефинирана като един голям речник, който свързва действия (нови факти, които трябва да бъдат добавени в работната памет) с условия, изразени като AND-OR изрази. Също така, някои факти могат да бъдат `Запитани`.\n"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {
"trusted": true
},
"outputs": [],
"source": [
"rules = {\n",
" 'default': Ask(['y','n']),\n",
" 'color' : Ask(['red-brown','black and white','other']),\n",
" 'pattern' : Ask(['dark stripes','dark spots']),\n",
" 'mammal': If(OR(['hair','gives milk'])),\n",
" 'carnivor': If(OR([AND(['sharp teeth','claws','forward-looking eyes']),'eats meat'])),\n",
" 'ungulate': If(['mammal',OR(['has hooves','chews cud'])]),\n",
" 'bird': If(OR(['feathers',AND(['flies','lies eggs'])])),\n",
" 'animal:monkey' : If(['mammal','carnivor','color:red-brown','pattern:dark spots']),\n",
" 'animal:tiger' : If(['mammal','carnivor','color:red-brown','pattern:dark stripes']),\n",
" 'animal:giraffe' : If(['ungulate','long neck','long legs','pattern:dark spots']),\n",
" 'animal:zebra' : If(['ungulate','pattern:dark stripes']),\n",
" 'animal:ostrich' : If(['bird','long nech','color:black and white','cannot fly']),\n",
" 'animal:pinguin' : If(['bird','swims','color:black and white','cannot fly']),\n",
" 'animal:albatross' : If(['bird','flies well'])\n",
"}"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"За да извършим обратното заключение, ще дефинираме клас `Knowledgebase`. Той ще съдържа:\n",
"* Работеща `памет` - речник, който свързва атрибути със стойности\n",
"* Правила на `Knowledgebase` във формат, както е определено по-горе\n",
"\n",
"Двата основни метода са:\n",
"* `get` за получаване на стойността на атрибут, като се извършва заключение, ако е необходимо. Например, `get('color')` ще получи стойността на слот за цвят (ще попита, ако е необходимо, и ще съхрани стойността за по-късна употреба в работещата памет). Ако попитаме `get('color:blue')`, ще попита за цвят и след това ще върне стойност `y`/`n` в зависимост от цвета.\n",
"* `eval` извършва действителното заключение, т.е. преминава през AND/OR дървото, оценява подцели и т.н.\n"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {
"trusted": true
},
"outputs": [],
"source": [
"class KnowledgeBase():\n",
" def __init__(self,rules):\n",
" self.rules = rules\n",
" self.memory = {}\n",
" \n",
" def get(self,name):\n",
" if ':' in name:\n",
" k,v = name.split(':')\n",
" vv = self.get(k)\n",
" return 'y' if v==vv else 'n'\n",
" if name in self.memory.keys():\n",
" return self.memory[name]\n",
" for fld in self.rules.keys():\n",
" if fld==name or fld.startswith(name+\":\"):\n",
" # print(\" + proving {}\".format(fld))\n",
" value = 'y' if fld==name else fld.split(':')[1]\n",
" res = self.eval(self.rules[fld],field=name)\n",
" if res!='y' and res!='n' and value=='y':\n",
" self.memory[name] = res\n",
" return res\n",
" if res=='y':\n",
" self.memory[name] = value\n",
" return value\n",
" # field is not found, using default\n",
" res = self.eval(self.rules['default'],field=name)\n",
" self.memory[name]=res\n",
" return res\n",
" \n",
" def eval(self,expr,field=None):\n",
" # print(\" + eval {}\".format(expr))\n",
" if isinstance(expr,Ask):\n",
" print(field)\n",
" return expr.ask()\n",
" elif isinstance(expr,If):\n",
" return self.eval(expr.x)\n",
" elif isinstance(expr,AND) or isinstance(expr,list):\n",
" expr = expr.x if isinstance(expr,AND) else expr\n",
" for x in expr:\n",
" if self.eval(x)=='n':\n",
" return 'n'\n",
" return 'y'\n",
" elif isinstance(expr,OR):\n",
" for x in expr.x:\n",
" if self.eval(x)=='y':\n",
" return 'y'\n",
" return 'n'\n",
" elif isinstance(expr,str):\n",
" return self.get(expr)\n",
" else:\n",
" print(\"Unknown expr: {}\".format(expr))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Сега нека дефинираме нашата база знания за животни и проведем консултацията. Имайте предвид, че това извикване ще ви задава въпроси. Можете да отговорите, като напишете `y`/`n` за въпроси с отговор да-не, или като посочите число (0..N) за въпроси с по-дълги отговори с множество опции.\n"
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {
"trusted": true
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"hair\n",
"y/n\n",
"sharp teeth\n",
"y/n\n",
"claws\n",
"y/n\n",
"forward-looking eyes\n",
"y/n\n",
"color\n",
"0. red-brown\n",
"1. black and white\n",
"2. other\n",
"has hooves\n",
"y/n\n",
"long neck\n",
"y/n\n",
"long legs\n",
"y/n\n",
"pattern\n",
"0. dark stripes\n",
"1. dark spots\n"
]
},
{
"data": {
"text/plain": [
"'giraffe'"
]
},
"execution_count": 4,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"kb = KnowledgeBase(rules)\n",
"kb.get('animal')"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Използване на PyKnow за напредващо заключение\n",
"\n",
"В следващия пример ще се опитаме да реализираме напредващо заключение, използвайки една от библиотеките за представяне на знания, [PyKnow](https://github.com/buguroo/pyknow/). **PyKnow** е библиотека за създаване на системи за напредващо заключение в Python, която е проектирана да бъде подобна на класическата стара система [CLIPS](http://www.clipsrules.net/index.html).\n",
"\n",
"Можехме също така да реализираме напредващо свързване сами без особени затруднения, но наивните реализации обикновено не са много ефективни. За по-ефективно съвпадение на правила се използва специален алгоритъм [Rete](https://en.wikipedia.org/wiki/Rete_algorithm).\n"
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {
"trusted": true
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Collecting git+https://github.com/buguroo/pyknow/\n",
" Cloning https://github.com/buguroo/pyknow/ to /tmp/pip-req-build-3cqeulyl\n",
" Running command git clone --filter=blob:none --quiet https://github.com/buguroo/pyknow/ /tmp/pip-req-build-3cqeulyl\n",
" Resolved https://github.com/buguroo/pyknow/ to commit 48818336f2e9a126f1964f2d8dc22d37ff800fe8\n",
" Preparing metadata (setup.py) ... \u001b[?25ldone\n",
"\u001b[?25hCollecting frozendict==1.2\n",
" Using cached frozendict-1.2.tar.gz (2.6 kB)\n",
" Preparing metadata (setup.py) ... \u001b[?25ldone\n",
"\u001b[?25hCollecting schema==0.6.7\n",
" Using cached schema-0.6.7-py2.py3-none-any.whl (14 kB)\n",
"Building wheels for collected packages: pyknow, frozendict\n",
" Building wheel for pyknow (setup.py) ... \u001b[?25ldone\n",
"\u001b[?25h Created wheel for pyknow: filename=pyknow-1.7.0-py3-none-any.whl size=34228 sha256=b7de5b09292c4007667c72f69b98d5a1b5f7324ff15f9dd8e077c3d5f7aade42\n",
" Stored in directory: /tmp/pip-ephem-wheel-cache-k7jpave7/wheels/81/1a/d3/f6c15dbe1955598a37755215f2a10449e7418500d7bd4b9508\n",
" Building wheel for frozendict (setup.py) ... \u001b[?25ldone\n",
"\u001b[?25h Created wheel for frozendict: filename=frozendict-1.2-py3-none-any.whl size=3148 sha256=2863d55c240d2409cddf05ccfe600591f8478681549fc97555c47c90dc6bb160\n",
" Stored in directory: /home/rg/.cache/pip/wheels/49/ac/f8/cb8120244e710bdb479c86198b03c7b08c3c2d3d2bf448fd6e\n",
"Successfully built pyknow frozendict\n",
"Installing collected packages: schema, frozendict, pyknow\n",
"Successfully installed frozendict-1.2 pyknow-1.7.0 schema-0.6.7\n"
]
}
],
"source": [
"import sys\n",
"!{sys.executable} -m pip install git+https://github.com/buguroo/pyknow/"
]
},
{
"cell_type": "code",
"execution_count": 13,
"metadata": {
"trusted": true
},
"outputs": [],
"source": [
"from pyknow import *\n",
"#import pyknow"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Ще дефинираме нашата система като клас, който наследява `KnowledgeEngine`. Всяко правило се дефинира чрез отделна функция с анотация `@Rule`, която указва кога правилото трябва да се изпълни. Вътре в правилото можем да добавяме нови факти, използвайки функцията `declare`, и добавянето на тези факти ще доведе до извикване на още правила от двигателя за напредващо заключение.\n"
]
},
{
"cell_type": "code",
"execution_count": 14,
"metadata": {
"trusted": true
},
"outputs": [],
"source": [
"class Animals(KnowledgeEngine):\n",
" @Rule(OR(\n",
" AND(Fact('sharp teeth'),Fact('claws'),Fact('forward looking eyes')),\n",
" Fact('eats meat')))\n",
" def cornivor(self):\n",
" self.declare(Fact('carnivor'))\n",
" \n",
" @Rule(OR(Fact('hair'),Fact('gives milk')))\n",
" def mammal(self):\n",
" self.declare(Fact('mammal'))\n",
"\n",
" @Rule(Fact('mammal'),\n",
" OR(Fact('has hooves'),Fact('chews cud')))\n",
" def hooves(self):\n",
" self.declare('ungulate')\n",
" \n",
" @Rule(OR(Fact('feathers'),AND(Fact('flies'),Fact('lays eggs'))))\n",
" def bird(self):\n",
" self.declare('bird')\n",
" \n",
" @Rule(Fact('mammal'),Fact('carnivor'),\n",
" Fact(color='red-brown'),\n",
" Fact(pattern='dark spots'))\n",
" def monkey(self):\n",
" self.declare(Fact(animal='monkey'))\n",
"\n",
" @Rule(Fact('mammal'),Fact('carnivor'),\n",
" Fact(color='red-brown'),\n",
" Fact(pattern='dark stripes'))\n",
" def tiger(self):\n",
" self.declare(Fact(animal='tiger'))\n",
"\n",
" @Rule(Fact('ungulate'),\n",
" Fact('long neck'),\n",
" Fact('long legs'),\n",
" Fact(pattern='dark spots'))\n",
" def giraffe(self):\n",
" self.declare(Fact(animal='giraffe'))\n",
"\n",
" @Rule(Fact('ungulate'),\n",
" Fact(pattern='dark stripes'))\n",
" def zebra(self):\n",
" self.declare(Fact(animal='zebra'))\n",
"\n",
" @Rule(Fact('bird'),\n",
" Fact('long neck'),\n",
" Fact('cannot fly'),\n",
" Fact(color='black and white'))\n",
" def straus(self):\n",
" self.declare(Fact(animal='ostrich'))\n",
"\n",
" @Rule(Fact('bird'),\n",
" Fact('swims'),\n",
" Fact('cannot fly'),\n",
" Fact(color='black and white'))\n",
" def pinguin(self):\n",
" self.declare(Fact(animal='pinguin'))\n",
"\n",
" @Rule(Fact('bird'),\n",
" Fact('flies well'))\n",
" def albatros(self):\n",
" self.declare(Fact(animal='albatross'))\n",
" \n",
" @Rule(Fact(animal=MATCH.a))\n",
" def print_result(self,a):\n",
" print('Animal is {}'.format(a))\n",
" \n",
" def factz(self,l):\n",
" for x in l:\n",
" self.declare(x)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"След като сме дефинирали база от знания, попълваме работната памет с някои начални факти и след това извикваме метода `run()`, за да извършим изводите. Можете да видите като резултат, че нови изведени факти се добавят към работната памет, включително крайния факт за животното (ако сме задали всички начални факти правилно).\n"
]
},
{
"cell_type": "code",
"execution_count": 15,
"metadata": {
"trusted": true
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Animal is tiger\n"
]
},
{
"data": {
"text/plain": [
"FactList([(0, InitialFact()),\n",
" (1, Fact(color='red-brown')),\n",
" (2, Fact(pattern='dark stripes')),\n",
" (3, Fact('sharp teeth')),\n",
" (4, Fact('claws')),\n",
" (5, Fact('forward looking eyes')),\n",
" (6, Fact('gives milk')),\n",
" (7, Fact('mammal')),\n",
" (8, Fact('carnivor')),\n",
" (9, Fact(animal='tiger'))])"
]
},
"execution_count": 15,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"ex1 = Animals()\n",
"ex1.reset()\n",
"ex1.factz([\n",
" Fact(color='red-brown'),\n",
" Fact(pattern='dark stripes'),\n",
" Fact('sharp teeth'),\n",
" Fact('claws'),\n",
" Fact('forward looking eyes'),\n",
" Fact('gives milk')])\n",
"ex1.run()\n",
"ex1.facts"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": []
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"\n---\n\n**Отказ от отговорност**: \nТози документ е преведен с помощта на AI услуга за превод [Co-op Translator](https://github.com/Azure/co-op-translator). Въпреки че се стремим към точност, моля, имайте предвид, че автоматизираните преводи може да съдържат грешки или неточности. Оригиналният документ на неговия роден език трябва да се счита за авторитетен източник. За критична информация се препоръчва професионален човешки превод. Ние не носим отговорност за недоразумения или погрешни интерпретации, произтичащи от използването на този превод.\n"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3.7.4 64-bit (conda)",
"metadata": {
"interpreter": {
"hash": "86193a1ab0ba47eac1c69c1756090baa3b420b3eea7d4aafab8b85f8b312f0c5"
}
},
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.11.2"
},
"coopTranslator": {
"original_hash": "ab2bd97b0453415b89a469284609a8ce",
"translation_date": "2025-08-29T23:58:48+00:00",
"source_file": "lessons/2-Symbolic/Animals.ipynb",
"language_code": "bg"
}
},
"nbformat": 4,
"nbformat_minor": 2
}

View File

@ -0,0 +1,595 @@
{
"cells": [
{
"cell_type": "markdown",
"metadata": {
"collapsed": true
},
"source": [
"# Онтология на семейните взаимоотношения\n",
"\n",
"Този пример е част от [Учебната програма за начинаещи в AI](http://github.com/microsoft/ai-for-beginners) и е вдъхновен от [тази блог публикация](https://habr.com/post/270857/).\n",
"\n",
"Винаги ми е трудно да запомня различните взаимоотношения между хората в едно семейство. В този пример ще използваме онтология, която дефинира семейните взаимоотношения, и реалното родословно дърво, за да покажем как можем автоматично да извлечем информация за всички роднини.\n",
"\n",
"### Получаване на родословното дърво\n",
"\n",
"Като пример ще използваме родословното дърво на [семейството на Романовите](https://en.wikipedia.org/wiki/House_of_Romanov). Най-често срещаният формат за описание на семейни взаимоотношения е [GEDCOM](https://en.wikipedia.org/wiki/GEDCOM). Ще вземем родословното дърво на Романовите във формат GEDCOM:\n"
]
},
{
"cell_type": "code",
"execution_count": 1,
"metadata": {
"trusted": true
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"0 HEAD\n",
"1 CHAR UTF8\n",
"1 GEDC\n",
"2 VERS 5.5\n",
"0 @0@ INDI\n",
"1 NAME Mihail Fedorovich /Romanov/\n",
"1 SEX M\n",
"1 BIRT\n",
"2 DATE 1613\n",
"1 DEAT \n",
"2 DATE 1645\n",
"1 FAMS @41@\n",
"0 @1@ INDI\n",
"1 NAME Evdokija Lukjanovna /Streshneva/\n",
"1 SEX F\n"
]
}
],
"source": [
"!head -15 data/tsars.ged"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"За да използваме GEDCOM файл, можем да използваме библиотеката `python-gedcom`:\n"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {
"trusted": true
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Collecting python-gedcom\n",
" Downloading python_gedcom-1.0.0-py2.py3-none-any.whl (35 kB)\n",
"Installing collected packages: python-gedcom\n",
"Successfully installed python-gedcom-1.0.0\n"
]
}
],
"source": [
"import sys\n",
"!{sys.executable} -m pip install python-gedcom"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Тази библиотека премахва някои от техническите проблеми с анализа на файлове, но все пак ни предоставя достъп на доста ниско ниво до всички индивиди и семейства в дървото. Ето как можем да анализираме файла и да покажем списъка на всички индивиди:\n"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {
"trusted": true
},
"outputs": [],
"source": [
"from gedcom.parser import Parser\n",
"from gedcom.element.individual import IndividualElement\n",
"from gedcom.element.family import FamilyElement\n",
"g = Parser()\n",
"g.parse_file('data/tsars.ged')"
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {
"scrolled": true,
"trusted": true
},
"outputs": [
{
"data": {
"text/plain": [
"[('@0@', ('Mihail Fedorovich', 'Romanov')),\n",
" ('@1@', ('Evdokija Lukjanovna', 'Streshneva')),\n",
" ('@2@', ('Aleksej Mihajlovich', 'Romanov')),\n",
" ('@3@', ('Marija Ilinichna', 'Miloslavskaja')),\n",
" ('@4@', ('Natalja Kirillovna', 'Naryshkina')),\n",
" ('@5@', ('Marfa Matveevna', 'Apraksina')),\n",
" ('@6@', ('Fedor Alekseevich', 'Romanov')),\n",
" ('@7@', ('Sofja Aleksevna', 'Romanova')),\n",
" ('@8@', ('Ivan V Alekseevich', 'Romanov')),\n",
" ('@9@', ('Praskovja Fedorovna', 'Saltykova')),\n",
" ('@10@', ('Ekaterina Ivanovna', 'Romanova')),\n",
" ('@11@', ('Anna Ivanovna', 'Romanova')),\n",
" ('@12@', ('Fridrih Vilgelm', 'Kurlandskij')),\n",
" ('@13@', ('Karl Leopold', 'Meklenburg-Shverinskij')),\n",
" ('@14@', ('Anna Leopoldovna', 'Meklenburg-Shverinskaja')),\n",
" ('@15@', ('Anton Ulrih', 'Braunshvejg-Volfenbjuttelskij')),\n",
" ('@16@', ('Ivan VI Antonovich', 'Braunshvejg-Volfenbjuttelskij')),\n",
" ('@17@', ('Petr I Alekseevich', 'Romanov')),\n",
" ('@18@', ('Evdokija Fedorovna', 'Lopuhina')),\n",
" ('@19@', ('Ekaterina I Alekseevna', 'Mihajlova')),\n",
" ('@20@', ('Aleksej Petrovich', 'Romanov')),\n",
" ('@21@', ('Sharlotta Kristina', 'Braunshvejg-Volfenbjuttelskaja')),\n",
" ('@22@', ('Petr II Alekseevich', 'Romanov')),\n",
" ('@23@', ('Anna Petrovna', 'Romanova')),\n",
" ('@24@', ('Elizaveta Petrovna', 'Romanova')),\n",
" ('@25@', ('Karl Fridrih', 'Golshtejn-Gottorpskij')),\n",
" ('@26@', ('Petr III Fedorovich', 'Romanov')),\n",
" ('@27@', ('Ekaterina II', 'Alekseevna')),\n",
" ('@28@', ('Pavel I Petrovich', 'Romanov')),\n",
" ('@29@', ('Natalja Alekseevna', 'Gessen-Darmshtadskaja')),\n",
" ('@30@', ('Marija Fedorovna', 'Vjurtembergskaja')),\n",
" ('@31@', ('Aleksandr I Pavlovich', 'Romanov')),\n",
" ('@32@', ('Elizaveta Alekseevna', 'Baden-Durlahskaja')),\n",
" ('@33@', ('Nikolaj I Pavlovich', 'Romanov')),\n",
" ('@34@', ('Aleksandra Fedorovna', 'Prusskaja')),\n",
" ('@35@', ('Aleksandr II Nikolaevich', 'Romanov')),\n",
" ('@36@', ('Marija Aleksandrovna', 'Gessenskaja')),\n",
" ('@37@', ('Aleksandr III Aleksandrovich', 'Romanov')),\n",
" ('@38@', ('Marija Fedorovna', 'Datskaja')),\n",
" ('@39@', ('Nikolaj II Aleksandrovich', 'Romanov')),\n",
" ('@40@', ('Aleksandra Fedorovna', 'Gessenskaja'))]"
]
},
"execution_count": 4,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"d = g.get_element_dictionary()\n",
"[ (k,v.get_name()) for k,v in d.items() if isinstance(v,IndividualElement)]"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Ето как можем да получим информация за семейства. Обърнете внимание, че това ни дава списък с **идентификатори**, и трябва да ги преобразуваме в имена, ако искаме повече яснота:\n"
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"[('@41@', ['@0@', '@1@', '@2@']),\n",
" ('@42@', ['@2@', '@3@', '@6@', '@7@', '@8@']),\n",
" ('@43@', ['@8@', '@9@', '@10@', '@11@']),\n",
" ('@44@', ['@13@', '@10@', '@14@']),\n",
" ('@45@', ['@15@', '@14@', '@16@']),\n",
" ('@46@', ['@2@', '@4@', '@17@']),\n",
" ('@47@', ['@17@', '@18@', '@20@']),\n",
" ('@48@', ['@20@', '@21@', '@22@']),\n",
" ('@49@', ['@17@', '@19@', '@23@', '@24@']),\n",
" ('@50@', ['@25@', '@23@', '@26@']),\n",
" ('@51@', ['@26@', '@27@', '@28@']),\n",
" ('@52@', ['@28@', '@30@', '@31@', '@33@']),\n",
" ('@53@', ['@33@', '@34@', '@35@']),\n",
" ('@54@', ['@35@', '@36@', '@37@']),\n",
" ('@55@', ['@37@', '@38@', '@39@'])]"
]
},
"execution_count": 5,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"d = g.get_element_dictionary()\n",
"[ (k,[x.get_value() for x in v.get_child_elements()]) for k,v in d.items() if isinstance(v,FamilyElement)]"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Получаване на семейна онтология\n",
"\n",
"След това нека разгледаме [семейната онтология](https://raw.githubusercontent.com/blokhin/genealogical-trees/master/data/header.ttl), дефинирана като набор от триплети на Семантичната мрежа. Тази онтология определя взаимоотношения като `isUncleOf`, `isCousinOf` и много други. Всички тези взаимоотношения са дефинирани чрез основни предикати като `isMotherOf`, `isFatherOf`, `isBrotherOf` и `isSisterOf`. Ще използваме автоматично разсъждение, за да изведем всички останали взаимоотношения, използвайки онтологията.\n",
"\n",
"Ето примерна дефиниция на свойството `isAuntOf`, което е дефинирано като композиция на `isSisterOf` и `isParentOf` (*Леля е сестра на родителя на дадено лице*).\n",
"\n",
"```\n",
"fhkb:isAuntOf a owl:ObjectProperty ;\n",
" rdfs:domain fhkb:Woman ;\n",
" rdfs:range fhkb:Person ;\n",
" owl:propertyChainAxiom ( fhkb:isSisterOf fhkb:isParentOf ) .\n",
"```\n"
]
},
{
"cell_type": "code",
"execution_count": 6,
"metadata": {
"trusted": true
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"@prefix fhkb: <http://www.example.com/genealogy.owl#> .\n",
"@prefix owl: <http://www.w3.org/2002/07/owl#> .\n",
"@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .\n",
"@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .\n",
"@prefix xml: <http://www.w3.org/XML/1998/namespace> .\n",
"@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .\n",
"\n",
"<http://www.example.com/genealogy.owl#> a owl:Ontology .\n",
"\n",
"fhkb:DomainEntity a owl:Class .\n",
"\n",
"fhkb:Man a owl:Class ;\n",
" owl:equivalentClass [ a owl:Class ;\n",
" owl:intersectionOf ( fhkb:Person [ a owl:Restriction ;\n",
" owl:onProperty fhkb:hasSex ;\n",
" owl:someValuesFrom fhkb:Male ] ) ] .\n",
"\n",
"fhkb:Woman a owl:Class ;\n",
" owl:equivalentClass [ a owl:Class ;\n",
" owl:intersectionOf ( fhkb:Person [ a owl:Restriction ;\n"
]
}
],
"source": [
"!head -20 data/onto.ttl"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Създаване на онтология за извеждане\n",
"\n",
"За улеснение ще създадем един файл с онтология, който ще включва оригиналните правила от семейната онтология и факти за индивидите от нашия GEDCOM файл. Ще прегледаме GEDCOM файла, ще извлечем информация за семействата и индивидите и ще я преобразуваме в триплети.\n"
]
},
{
"cell_type": "code",
"execution_count": 7,
"metadata": {
"trusted": true
},
"outputs": [],
"source": [
"!cp data/onto.ttl .\n",
"\n",
"gedcom_dict = g.get_element_dictionary()\n",
"individuals, marriages = {}, {}\n",
"\n",
"def term2id(el):\n",
" return \"i\" + el.get_pointer().replace('@', '').lower()\n",
"\n",
"out = open(\"onto.ttl\",\"a\")\n",
"\n",
"for k, v in gedcom_dict.items():\n",
" if isinstance(v,IndividualElement):\n",
" children, siblings = set(), set()\n",
" idx = term2id(v)\n",
"\n",
" title = v.get_name()[0] + \" \" + v.get_name()[1]\n",
" title = title.replace('\"', '').replace('[', '').replace(']', '').replace('(', '').replace(')', '').strip()\n",
"\n",
" own_families = g.get_families(v, 'FAMS')\n",
" for fam in own_families:\n",
" children |= set(term2id(i) for i in g.get_family_members(fam, \"CHIL\"))\n",
"\n",
" parent_families = g.get_families(v, 'FAMC')\n",
" if len(parent_families):\n",
" for member in g.get_family_members(parent_families[0], \"CHIL\"): # NB adoptive families i.e len(parent_families)>1 are not considered (TODO?)\n",
" if member.get_pointer() == v.get_pointer():\n",
" continue\n",
" siblings.add(term2id(member))\n",
"\n",
" if idx in individuals:\n",
" children |= individuals[idx].get('children', set())\n",
" siblings |= individuals[idx].get('siblings', set())\n",
" individuals[idx] = {'sex': v.get_gender().lower(), 'children': children, 'siblings': siblings, 'title': title}\n",
"\n",
" elif isinstance(v,FamilyElement):\n",
" wife, husb, children = None, None, set()\n",
" children = set(term2id(i) for i in g.get_family_members(v, \"CHIL\"))\n",
"\n",
" try:\n",
" wife = g.get_family_members(v, \"WIFE\")[0]\n",
" wife = term2id(wife)\n",
" if wife in individuals: individuals[wife]['children'] |= children\n",
" else: individuals[wife] = {'children': children}\n",
" except IndexError: pass\n",
" try:\n",
" husb = g.get_family_members(v, \"HUSB\")[0]\n",
" husb = term2id(husb)\n",
" if husb in individuals: individuals[husb]['children'] |= children\n",
" else: individuals[husb] = {'children': children}\n",
" except IndexError: pass\n",
"\n",
" if wife and husb: marriages[wife + husb] = (term2id(v), wife, husb)\n",
"\n",
"for idx, val in individuals.items():\n",
" added_terms = ''\n",
" if val['sex'] == 'f':\n",
" parent_predicate, sibl_predicate = \"isMotherOf\", \"isSisterOf\"\n",
" else:\n",
" parent_predicate, sibl_predicate = \"isFatherOf\", \"isBrotherOf\"\n",
" if len(val['children']):\n",
" added_terms += \" ;\\n fhkb:\" + parent_predicate + \" \" + \", \".join([\"fhkb:\" + i for i in val['children']])\n",
" if len(val['siblings']):\n",
" added_terms += \" ;\\n fhkb:\" + sibl_predicate + \" \" + \", \".join([\"fhkb:\" + i for i in val['siblings']])\n",
" out.write(\"fhkb:%s a owl:NamedIndividual, owl:Thing%s ;\\n rdfs:label \\\"%s\\\" .\\n\" % (idx, added_terms, val['title']))\n",
"\n",
"for k, v in marriages.items():\n",
" out.write(\"fhkb:%s a owl:NamedIndividual, owl:Thing ;\\n fhkb:hasFemalePartner fhkb:%s ;\\n fhkb:hasMalePartner fhkb:%s .\\n\" % v)\n",
"\n",
"out.write(\"[] a owl:AllDifferent ;\\n owl:distinctMembers (\")\n",
"for idx in individuals.keys():\n",
" out.write(\" fhkb:\" + idx)\n",
"for k, v in marriages.items():\n",
" out.write(\" fhkb:\" + v[0])\n",
"out.write(\" ) .\")\n",
"out.close()"
]
},
{
"cell_type": "code",
"execution_count": 8,
"metadata": {
"trusted": true
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
" fhkb:hasFemalePartner fhkb:i34 ;\n",
" fhkb:hasMalePartner fhkb:i33 .\n",
"fhkb:i54 a owl:NamedIndividual, owl:Thing ;\n",
" fhkb:hasFemalePartner fhkb:i36 ;\n",
" fhkb:hasMalePartner fhkb:i35 .\n",
"fhkb:i55 a owl:NamedIndividual, owl:Thing ;\n",
" fhkb:hasFemalePartner fhkb:i38 ;\n",
" fhkb:hasMalePartner fhkb:i37 .\n",
"[] a owl:AllDifferent ;\n",
" owl:distinctMembers ( fhkb:i0 fhkb:i1 fhkb:i2 fhkb:i3 fhkb:i4 fhkb:i5 fhkb:i6 fhkb:i7 fhkb:i8 fhkb:i9 fhkb:i10 fhkb:i11 fhkb:i12 fhkb:i13 fhkb:i14 fhkb:i15 fhkb:i16 fhkb:i17 fhkb:i18 fhkb:i19 fhkb:i20 fhkb:i21 fhkb:i22 fhkb:i23 fhkb:i24 fhkb:i25 fhkb:i26 fhkb:i27 fhkb:i28 fhkb:i29 fhkb:i30 fhkb:i31 fhkb:i32 fhkb:i33 fhkb:i34 fhkb:i35 fhkb:i36 fhkb:i37 fhkb:i38 fhkb:i39 fhkb:i40 fhkb:i41 fhkb:i42 fhkb:i43 fhkb:i44 fhkb:i45 fhkb:i46 fhkb:i47 fhkb:i48 fhkb:i49 fhkb:i50 fhkb:i51 fhkb:i52 fhkb:i53 fhkb:i54 fhkb:i55 ) ."
]
}
],
"source": [
"!tail onto.ttl"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Извършване на изводи\n",
"\n",
"Сега искаме да можем да използваме тази онтология за извеждане и за заявки. Ще използваме [RDFLib](https://github.com/RDFLib), библиотека за четене на RDF графи в различни формати, изпълнение на заявки и други.\n",
"\n",
"За логическо извеждане ще използваме библиотеката [OWL-RL](https://github.com/RDFLib/OWL-RL), която ни позволява да изградим **Closure** на RDF графа, т.е. да добавим всички възможни концепции и връзки, които могат да бъдат изведени.\n"
]
},
{
"cell_type": "code",
"execution_count": 10,
"metadata": {
"trusted": true
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Requirement already satisfied: rdflib in /home/rg/anaconda3/envs/ai4beg/lib/python3.11/site-packages (6.3.2)\n",
"Requirement already satisfied: isodate<0.7.0,>=0.6.0 in /home/rg/anaconda3/envs/ai4beg/lib/python3.11/site-packages (from rdflib) (0.6.1)\n",
"Requirement already satisfied: pyparsing<4,>=2.1.0 in /home/rg/anaconda3/envs/ai4beg/lib/python3.11/site-packages (from rdflib) (3.0.9)\n",
"Requirement already satisfied: six in /home/rg/anaconda3/envs/ai4beg/lib/python3.11/site-packages (from isodate<0.7.0,>=0.6.0->rdflib) (1.16.0)\n",
"Collecting git+https://github.com/RDFLib/OWL-RL.git\n",
" Cloning https://github.com/RDFLib/OWL-RL.git to /tmp/pip-req-build-lbfzwi3m\n",
" Running command git clone --filter=blob:none --quiet https://github.com/RDFLib/OWL-RL.git /tmp/pip-req-build-lbfzwi3m\n",
" Resolved https://github.com/RDFLib/OWL-RL.git to commit a77e1791b88b54aace609bc6000aac14c7add4ff\n",
" Preparing metadata (setup.py) ... \u001b[?25ldone\n",
"\u001b[?25hRequirement already satisfied: rdflib>=6.0.2 in /home/rg/anaconda3/envs/ai4beg/lib/python3.11/site-packages (from owlrl==6.0.2) (6.3.2)\n",
"Requirement already satisfied: isodate<0.7.0,>=0.6.0 in /home/rg/anaconda3/envs/ai4beg/lib/python3.11/site-packages (from rdflib>=6.0.2->owlrl==6.0.2) (0.6.1)\n",
"Requirement already satisfied: pyparsing<4,>=2.1.0 in /home/rg/anaconda3/envs/ai4beg/lib/python3.11/site-packages (from rdflib>=6.0.2->owlrl==6.0.2) (3.0.9)\n",
"Requirement already satisfied: six in /home/rg/anaconda3/envs/ai4beg/lib/python3.11/site-packages (from isodate<0.7.0,>=0.6.0->rdflib>=6.0.2->owlrl==6.0.2) (1.16.0)\n"
]
}
],
"source": [
"!{sys.executable} -m pip install rdflib\n",
"!{sys.executable} -m pip install git+https://github.com/RDFLib/OWL-RL.git"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Нека отворим файла с онтологията и да видим колко триплета съдържа:\n"
]
},
{
"cell_type": "code",
"execution_count": 11,
"metadata": {
"trusted": true
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Triplets found:669\n"
]
}
],
"source": [
"import rdflib\n",
"from owlrl import DeductiveClosure, OWLRL_Extension\n",
"\n",
"g = rdflib.Graph()\n",
"g.parse(\"onto.ttl\", format=\"turtle\")\n",
"\n",
"print(\"Triplets found:%d\" % len(g))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Сега нека изградим затварянето и да видим как броят на тройките се увеличава:\n"
]
},
{
"cell_type": "code",
"execution_count": 12,
"metadata": {
"trusted": true
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Triplets after inference:4246\n"
]
}
],
"source": [
"DeductiveClosure(OWLRL_Extension).expand(g)\n",
"print(\"Triplets after inference:%d\" % len(g))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Запитване за роднини\n",
"\n",
"Сега можем да направим запитване към графа, за да видим различни връзки между хората. Можем да използваме езика **SPARQL** заедно с метода `query`. В нашия случай, нека видим всички **чичовци** в нашето родословно дърво:\n"
]
},
{
"cell_type": "code",
"execution_count": 13,
"metadata": {
"trusted": true
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Fedor Alekseevich Romanov is uncle of Ekaterina Ivanovna Romanova\n",
"Aleksandr I Pavlovich Romanov is uncle of Aleksandr II Nikolaevich Romanov\n",
"Fedor Alekseevich Romanov is uncle of Anna Ivanovna Romanova\n"
]
}
],
"source": [
"qres = g.query(\n",
" \"\"\"SELECT DISTINCT ?aname ?bname\n",
" WHERE {\n",
" ?a fhkb:isUncleOf ?b .\n",
" ?a rdfs:label ?aname .\n",
" ?b rdfs:label ?bname .\n",
" }\"\"\")\n",
"\n",
"for row in qres:\n",
" print(\"%s is uncle of %s\" % row)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Чувствайте се свободни да експериментирате с различни други семейни връзки. Например, можете да разгледате връзката `isAncestorOf`, която рекурсивно определя всички предци на даден човек.\n",
"\n",
"Накрая, нека почистим!\n"
]
},
{
"cell_type": "code",
"execution_count": 14,
"metadata": {
"trusted": true
},
"outputs": [],
"source": [
"!rm onto.ttl"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": []
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"\n---\n\n**Отказ от отговорност**: \nТози документ е преведен с помощта на AI услуга за превод [Co-op Translator](https://github.com/Azure/co-op-translator). Въпреки че се стремим към точност, моля, имайте предвид, че автоматизираните преводи може да съдържат грешки или неточности. Оригиналният документ на неговия роден език трябва да се счита за авторитетен източник. За критична информация се препоръчва професионален човешки превод. Ние не носим отговорност за недоразумения или погрешни интерпретации, произтичащи от използването на този превод.\n"
]
}
],
"metadata": {
"interpreter": {
"hash": "86193a1ab0ba47eac1c69c1756090baa3b420b3eea7d4aafab8b85f8b312f0c5"
},
"kernelspec": {
"display_name": "Python 3.6",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.11.2"
},
"coopTranslator": {
"original_hash": "6537d5597320e27b6052b4377b8ff8bb",
"translation_date": "2025-08-29T23:56:00+00:00",
"source_file": "lessons/2-Symbolic/FamilyOntology.ipynb",
"language_code": "bg"
}
},
"nbformat": 4,
"nbformat_minor": 2
}

View File

@ -0,0 +1,548 @@
{
"cells": [
{
"cell_type": "markdown",
"metadata": {
"collapsed": true
},
"source": [
"## Microsoft Concept Graph\n",
"\n",
"[Microsoft Concept Graph](https://concept.research.microsoft.com/) е голяма таксономия от термини, извлечени от интернет, с `is-a` връзки между концепциите.\n",
"\n",
"Context Graph е наличен в две форми:\n",
" * Голям текстов файл за изтегляне\n",
" * REST API\n",
"\n",
"Статистика:\n",
" * 5401933 уникални концепции,\n",
" * 12551613 уникални инстанции\n",
" * 87603947 `is-a` връзки\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Използване на уеб услуга\n",
"\n",
"Уеб услугата предлага различни заявки за оценка на вероятността дадена концепция да принадлежи към различни групи. Повече информация е налична [тук](https://concept.research.microsoft.com/Home/Api). \n",
"Ето примерен URL за заявка: `https://concept.research.microsoft.com/api/Concept/ScoreByProb?instance=microsoft&topK=10`\n"
]
},
{
"cell_type": "code",
"execution_count": 7,
"metadata": {
"trusted": true
},
"outputs": [
{
"data": {
"text/plain": [
"{'company': 0.6105356614382954,\n",
" 'vendor': 0.08858636677518003,\n",
" 'client': 0.048239124001183784,\n",
" 'firm': 0.045476965571668145,\n",
" 'large company': 0.043109401203511886,\n",
" 'organization': 0.043010752688172046,\n",
" 'corporation': 0.035908059583703265,\n",
" 'brand': 0.03383644076156654,\n",
" 'software company': 0.027522935779816515,\n",
" 'technology company': 0.023774292196902438}"
]
},
"execution_count": 7,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"import urllib\n",
"import json\n",
"import ssl\n",
"\n",
"def http(x):\n",
" ssl._create_default_https_context = ssl._create_unverified_context\n",
" response = urllib.request.urlopen(x)\n",
" data = response.read()\n",
" return data.decode('utf-8')\n",
"\n",
"def query(x):\n",
" return json.loads(http(\"https://concept.research.microsoft.com/api/Concept/ScoreByProb?instance={}&topK=10\".format(urllib.parse.quote(x))))\n",
"\n",
"query('microsoft')"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Нека се опитаме да категоризираме заглавията на новините, използвайки основни концепции. За да получим заглавия на новини, ще използваме услугата [NewsApi.org](http://newsapi.org). Трябва да получите собствен API ключ, за да използвате услугата - отидете на уебсайта и се регистрирайте за безплатен план за разработчици.\n"
]
},
{
"cell_type": "code",
"execution_count": 20,
"metadata": {
"trusted": true
},
"outputs": [],
"source": [
"newsapi_key = '<your API key here>'\n",
"def get_news(country='us'):\n",
" res = json.loads(http(\"https://newsapi.org/v2/top-headlines?country={0}&apiKey={1}\".format(country,newsapi_key)))\n",
" return res['articles']\n",
"\n",
"all_titles = [x['title'] for x in get_news('us')+get_news('gb')]"
]
},
{
"cell_type": "code",
"execution_count": 21,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"['Covid-19 Live Updates: Vaccines and Boosters News - The New York Times',\n",
" 'Ukrainians Flee Mariupol as Russian Forces Push to Take Port City - The Wall Street Journal',\n",
" 'Bond Yields Jump, Stock Futures Rise After Powell Says Fed Is Ready to Be More Aggressive - The Wall Street Journal',\n",
" 'Putin critic Alexei Navalny found guilty by Russian court - New York Post ',\n",
" \"Supreme Court nominee Ketanji Brown Jackson will face questions at confirmation hearing's second day - CNN\",\n",
" '2 teachers killed at Swedish high school, student arrested - ABC News',\n",
" 'Clues to Covid-19s Next Moves Come From Sewers - The Wall Street Journal',\n",
" 'Republicans to roll dice by grilling Jackson over child-pornography sentencing decisions | TheHill - The Hill',\n",
" 'Clear sign Putin considering using chemical weapons in Ukraine, claims President Biden - The Independent',\n",
" 'NASA confirms there are 5,000 planets outside our solar system - Daily Mail',\n",
" \"US stocks whipsawed overnight after Fed Chair Powell's remarks - Fox Business\",\n",
" \"'We've learned absolutely nothing': Tests could again be in short supply if Covid surges - POLITICO\",\n",
" \"Duchess of Cambridge swaps khaki jungle gear for Vampire's Wife dress on Belize trip - Daily Mail\",\n",
" 'China searches for victims, flight recorders after first plane crash in 12 years - Reuters',\n",
" 'Second superyacht linked to Russian oligarch Abramovich docks in Turkey - Reuters',\n",
" 'Live updates: Russia stops talks with Japan over sanctions - The Associated Press - en Español',\n",
" 'Powers Remain and Threats Lurk as Womens Sweet 16 Is Set - The New York Times',\n",
" 'Webb Space Telescope Begins Multi-Instrument Alignment - SciTechDaily',\n",
" \"UConn vs UCF - NCAA women's tournament second-round highlights - March Madness\",\n",
" 'Bucking Republican Trend, Indiana Governor Vetoes Transgender Sports Bill - The New York Times',\n",
" \"Maggie Fox dead: Coronation Street and Shameless actress dies after 'sudden accident' - Mirror Online - The Mirror\",\n",
" 'China plane crash live: Search for survivors continues as witness describes moment flight fell from sky - The Independent',\n",
" 'Daniel Morgan murder: damning report condemns Met police - The Guardian',\n",
" 'What to expect from Rishi Sunaks Spring Statement - BBC.com',\n",
" 'UK and Republic of Ireland in line to host Euro 2028 after no one else bids - The Guardian',\n",
" \"Friends beg Vladimir Putin's 'lover' to persuade him to end Ukraine invasion - The Mirror\",\n",
" 'Brass Eyes outtakes show the brutal TV comedy was the tip of an iceberg - The Guardian',\n",
" \"Vladimir Putin threatens civilians to break Mariupol's spirit - The Times\",\n",
" 'Shell U-turn on Cambo oilfield would threaten green targets, say campaigners - The Guardian',\n",
" 'St Helens dog attack: Girl aged 17 months killed at home - BBC',\n",
" \"PlayStation to buy 'Assassin's Creed' veteran Jade Raymond's Haven Studios - NME\",\n",
" 'Clear sign Putin considering using chemical weapons in Ukraine, claims President Biden - The Independent',\n",
" 'NASA confirms there are 5,000 planets outside our solar system - Daily Mail',\n",
" 'Nintendo Switch finally has folders • Eurogamer.net - Eurogamer.net',\n",
" 'FA to “find a solution” as Liverpool fan group blasts “shambolic” Wembley travel - This Is Anfield',\n",
" 'Manchester United transfer news LIVE Erik ten Hag latest and Man Utd manager updates - Manchester Evening News',\n",
" 'Inflation raises cost of UK government borrowing in February; crude oil up again business live - The Guardian',\n",
" 'Alexei Navalny: Kremlin critic found guilty of large-scale fraud and contempt of court by Russian court - Sky News',\n",
" \"UK prepares to nationalize Russia natural gas giant Gazprom's retail unit - Business Insider\",\n",
" 'Zaghari-Ratcliffe: Hunt calls for inquiry into delay over Iran debt payment - The Guardian']"
]
},
"execution_count": 21,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"all_titles"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Първо, искаме да можем да извличаме съществителни от заглавия на новини. Ще използваме библиотеката `TextBlob`, за да направим това, която опростява много типични задачи в обработката на естествен език като тази.\n"
]
},
{
"cell_type": "code",
"execution_count": 15,
"metadata": {
"trusted": true
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Requirement already satisfied: textblob in c:\\winapp\\miniconda3\\lib\\site-packages (0.17.1)\n",
"Requirement already satisfied: nltk>=3.1 in c:\\winapp\\miniconda3\\lib\\site-packages (from textblob) (3.5)\n",
"Requirement already satisfied: joblib in c:\\winapp\\miniconda3\\lib\\site-packages (from nltk>=3.1->textblob) (1.0.1)\n",
"Requirement already satisfied: regex in c:\\winapp\\miniconda3\\lib\\site-packages (from nltk>=3.1->textblob) (2021.11.10)\n",
"Requirement already satisfied: tqdm in c:\\winapp\\miniconda3\\lib\\site-packages (from nltk>=3.1->textblob) (4.61.2)\n",
"Requirement already satisfied: click in c:\\winapp\\miniconda3\\lib\\site-packages (from nltk>=3.1->textblob) (8.0.3)\n",
"Requirement already satisfied: colorama in c:\\winapp\\miniconda3\\lib\\site-packages (from click->nltk>=3.1->textblob) (0.4.4)\n",
"Finished.\n"
]
},
{
"name": "stderr",
"output_type": "stream",
"text": [
"[nltk_data] Downloading package brown to\n",
"[nltk_data] C:\\Users\\dmitryso\\AppData\\Roaming\\nltk_data...\n",
"[nltk_data] Package brown is already up-to-date!\n",
"[nltk_data] Downloading package punkt to\n",
"[nltk_data] C:\\Users\\dmitryso\\AppData\\Roaming\\nltk_data...\n",
"[nltk_data] Package punkt is already up-to-date!\n",
"[nltk_data] Downloading package wordnet to\n",
"[nltk_data] C:\\Users\\dmitryso\\AppData\\Roaming\\nltk_data...\n",
"[nltk_data] Package wordnet is already up-to-date!\n",
"[nltk_data] Downloading package averaged_perceptron_tagger to\n",
"[nltk_data] C:\\Users\\dmitryso\\AppData\\Roaming\\nltk_data...\n",
"[nltk_data] Package averaged_perceptron_tagger is already up-to-\n",
"[nltk_data] date!\n",
"[nltk_data] Downloading package conll2000 to\n",
"[nltk_data] C:\\Users\\dmitryso\\AppData\\Roaming\\nltk_data...\n",
"[nltk_data] Package conll2000 is already up-to-date!\n",
"[nltk_data] Downloading package movie_reviews to\n",
"[nltk_data] C:\\Users\\dmitryso\\AppData\\Roaming\\nltk_data...\n",
"[nltk_data] Package movie_reviews is already up-to-date!\n"
]
}
],
"source": [
"import sys\n",
"!{sys.executable} -m pip install textblob\n",
"!{sys.executable} -m textblob.download_corpora\n",
"from textblob import TextBlob"
]
},
{
"cell_type": "code",
"execution_count": 22,
"metadata": {
"trusted": true
},
"outputs": [
{
"data": {
"text/plain": [
"{'covid-19 live updates': 1,\n",
" 'vaccines': 1,\n",
" 'boosters': 1,\n",
" 'york': 4,\n",
" 'ukrainians flee mariupol': 1,\n",
" 'forces push': 1,\n",
" 'port city': 1,\n",
" 'wall street journal': 3,\n",
" 'bond yields': 1,\n",
" 'futures rise': 1,\n",
" 'powell says fed': 1,\n",
" 'ready': 1,\n",
" 'be': 1,\n",
" 'aggressive': 1,\n",
" 'putin': 3,\n",
" 'alexei navalny': 2,\n",
" 'russian': 2,\n",
" 'supreme court nominee': 1,\n",
" 'ketanji brown jackson': 1,\n",
" \"confirmation hearing 's\": 1,\n",
" 'cnn': 1,\n",
" 'swedish': 1,\n",
" 'high school': 1,\n",
" 'abc': 1,\n",
" 'clues': 1,\n",
" 'covid-19': 1,\n",
" ' s': 2,\n",
" 'moves': 1,\n",
" 'sewers': 1,\n",
" 'roll dice': 1,\n",
" 'jackson': 1,\n",
" 'decisions |': 1,\n",
" 'thehill': 1,\n",
" 'clear': 2,\n",
" 'chemical weapons': 2,\n",
" 'ukraine': 3,\n",
" 'claims president': 2,\n",
" 'biden': 2,\n",
" 'nasa': 2,\n",
" 'solar system': 2,\n",
" 'daily mail': 3,\n",
" 'us stocks': 1,\n",
" 'fed chair powell': 1,\n",
" \"'s remarks\": 1,\n",
" 'fox': 1,\n",
" \"'we 've\": 1,\n",
" 'tests': 1,\n",
" 'covid': 1,\n",
" 'politico': 1,\n",
" 'duchess': 1,\n",
" 'cambridge': 1,\n",
" 'swaps khaki jungle gear': 1,\n",
" 'vampire': 1,\n",
" 'wife': 1,\n",
" 'belize': 1,\n",
" 'china': 2,\n",
" 'flight recorders': 1,\n",
" 'plane crash': 1,\n",
" 'reuters': 2,\n",
" 'russian oligarch': 1,\n",
" 'abramovich': 1,\n",
" 'live': 1,\n",
" 'russia': 2,\n",
" 'stops talks': 1,\n",
" 'japan': 1,\n",
" 'español': 1,\n",
" 'powers remain': 1,\n",
" 'threats lurk': 1,\n",
" 'set': 1,\n",
" 'webb': 1,\n",
" 'telescope begins multi-instrument alignment': 1,\n",
" 'scitechdaily': 1,\n",
" 'uconn': 1,\n",
" 'ucf': 1,\n",
" 'ncaa': 1,\n",
" \"women 's tournament second-round highlights\": 1,\n",
" 'march madness': 1,\n",
" 'bucking republican trend': 1,\n",
" 'indiana': 1,\n",
" 'vetoes transgender': 1,\n",
" 'bill': 1,\n",
" 'maggie fox': 1,\n",
" 'coronation': 1,\n",
" 'shameless': 1,\n",
" \"'sudden accident\": 1,\n",
" 'mirror online': 1,\n",
" 'mirror': 2,\n",
" 'plane crash ': 1,\n",
" 'search': 1,\n",
" 'moment flight': 1,\n",
" 'daniel morgan': 1,\n",
" 'report condemns': 1,\n",
" 'met': 1,\n",
" 'guardian': 6,\n",
" 'rishi sunak': 1,\n",
" ' s spring': 1,\n",
" 'statement': 1,\n",
" 'bbc.com': 1,\n",
" 'uk': 3,\n",
" 'ireland': 1,\n",
" 'euro': 1,\n",
" 'vladimir putin': 2,\n",
" \"'s 'lover\": 1,\n",
" 'brass eye': 1,\n",
" ' s outtakes': 1,\n",
" 'brutal tv comedy': 1,\n",
" 'threatens civilians': 1,\n",
" 'mariupol': 1,\n",
" \"'s spirit\": 1,\n",
" 'shell u-turn': 1,\n",
" 'cambo': 1,\n",
" 'green targets': 1,\n",
" 'st helens': 1,\n",
" 'dog attack': 1,\n",
" 'girl': 1,\n",
" 'bbc': 1,\n",
" 'playstation': 1,\n",
" \"'assassin 's\": 1,\n",
" 'creed': 1,\n",
" 'jade raymond': 1,\n",
" 'haven studios': 1,\n",
" 'nme': 1,\n",
" 'nintendo switch': 1,\n",
" 'folders •': 1,\n",
" 'eurogamer.net': 2,\n",
" 'fa': 1,\n",
" 'solution ”': 1,\n",
" 'liverpool': 1,\n",
" 'fan group blasts “ shambolic ”': 1,\n",
" 'wembley': 1,\n",
" 'anfield': 1,\n",
" 'manchester': 1,\n",
" 'live erik': 1,\n",
" 'hag': 1,\n",
" 'utd': 1,\n",
" 'manager updates': 1,\n",
" 'manchester evening': 1,\n",
" 'inflation': 1,\n",
" 'government borrowing': 1,\n",
" 'february': 1,\n",
" 'crude oil': 1,\n",
" ' business': 1,\n",
" 'kremlin': 1,\n",
" 'large-scale fraud': 1,\n",
" 'sky': 1,\n",
" 'natural gas': 1,\n",
" 'gazprom': 1,\n",
" 'retail unit': 1,\n",
" 'insider': 1,\n",
" 'zaghari-ratcliffe': 1,\n",
" 'hunt': 1,\n",
" 'iran': 1,\n",
" 'debt payment': 1}"
]
},
"execution_count": 22,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"w = {}\n",
"for x in all_titles:\n",
" for n in TextBlob(x).noun_phrases:\n",
" if n in w:\n",
" w[n].append(x)\n",
" else:\n",
" w[n]=[x]\n",
"{ x:len(w[x]) for x in w.keys()}"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Можем да видим, че съществителните не ни дават големи тематични групи. Нека заменим съществителните с по-общи термини, получени от графа на концепциите. Това ще отнеме известно време, защото правим REST заявка за всяка съществителна фраза.\n"
]
},
{
"cell_type": "code",
"execution_count": 23,
"metadata": {
"trusted": true
},
"outputs": [],
"source": [
"w = {}\n",
"for x in all_titles:\n",
" for noun in TextBlob(x).noun_phrases:\n",
" terms = query(noun.replace(' ','%20'))\n",
" for term in [u for u in terms.keys() if terms[u]>0.1]:\n",
" if term in w:\n",
" w[term].append(x)\n",
" else:\n",
" w[term]=[x]"
]
},
{
"cell_type": "code",
"execution_count": 24,
"metadata": {
"trusted": true
},
"outputs": [
{
"data": {
"text/plain": [
"{'city': 9,\n",
" 'brand': 4,\n",
" 'place': 9,\n",
" 'town': 4,\n",
" 'factor': 4,\n",
" 'film': 4,\n",
" 'nation': 11,\n",
" 'state': 5,\n",
" 'person': 4,\n",
" 'organization': 5,\n",
" 'publication': 10,\n",
" 'market': 5,\n",
" 'economy': 4,\n",
" 'company': 6,\n",
" 'newspaper': 6,\n",
" 'relationship': 6}"
]
},
"execution_count": 24,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"{ x:len(w[x]) for x in w.keys() if len(w[x])>3}"
]
},
{
"cell_type": "code",
"execution_count": 27,
"metadata": {
"trusted": true
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"\n",
"ECONOMY:\n",
"China searches for victims, flight recorders after first plane crash in 12 years - Reuters\n",
"Live updates: Russia stops talks with Japan over sanctions - The Associated Press - en Español\n",
"China plane crash live: Search for survivors continues as witness describes moment flight fell from sky - The Independent\n",
"UK prepares to nationalize Russia natural gas giant Gazprom's retail unit - Business Insider\n",
"\n",
"NATION:\n",
"Clear sign Putin considering using chemical weapons in Ukraine, claims President Biden - The Independent\n",
"Duchess of Cambridge swaps khaki jungle gear for Vampire's Wife dress on Belize trip - Daily Mail\n",
"China searches for victims, flight recorders after first plane crash in 12 years - Reuters\n",
"Live updates: Russia stops talks with Japan over sanctions - The Associated Press - en Español\n",
"Live updates: Russia stops talks with Japan over sanctions - The Associated Press - en Español\n",
"China plane crash live: Search for survivors continues as witness describes moment flight fell from sky - The Independent\n",
"UK and Republic of Ireland in line to host Euro 2028 after no one else bids - The Guardian\n",
"Friends beg Vladimir Putin's 'lover' to persuade him to end Ukraine invasion - The Mirror\n",
"Clear sign Putin considering using chemical weapons in Ukraine, claims President Biden - The Independent\n",
"UK prepares to nationalize Russia natural gas giant Gazprom's retail unit - Business Insider\n",
"Zaghari-Ratcliffe: Hunt calls for inquiry into delay over Iran debt payment - The Guardian\n",
"\n",
"PERSON:\n",
"Clear sign Putin considering using chemical weapons in Ukraine, claims President Biden - The Independent\n",
"Duchess of Cambridge swaps khaki jungle gear for Vampire's Wife dress on Belize trip - Daily Mail\n",
"Second superyacht linked to Russian oligarch Abramovich docks in Turkey - Reuters\n",
"Clear sign Putin considering using chemical weapons in Ukraine, claims President Biden - The Independent\n"
]
}
],
"source": [
"print('\\nECONOMY:\\n'+'\\n'.join(w['economy']))\n",
"print('\\nNATION:\\n'+'\\n'.join(w['nation']))\n",
"print('\\nPERSON:\\n'+'\\n'.join(w['person']))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"\n---\n\n**Отказ от отговорност**: \nТози документ е преведен с помощта на AI услуга за превод [Co-op Translator](https://github.com/Azure/co-op-translator). Въпреки че се стремим към точност, моля, имайте предвид, че автоматизираните преводи може да съдържат грешки или неточности. Оригиналният документ на неговия роден език трябва да се счита за авторитетен източник. За критична информация се препоръчва професионален човешки превод. Ние не носим отговорност за недоразумения или погрешни интерпретации, произтичащи от използването на този превод.\n"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3.7.4 64-bit (conda)",
"metadata": {
"interpreter": {
"hash": "86193a1ab0ba47eac1c69c1756090baa3b420b3eea7d4aafab8b85f8b312f0c5"
}
},
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.9.5"
},
"coopTranslator": {
"original_hash": "4087f998407d06ceb2947016ba4605d0",
"translation_date": "2025-08-29T23:57:15+00:00",
"source_file": "lessons/2-Symbolic/MSConceptGraph.ipynb",
"language_code": "bg"
}
},
"nbformat": 4,
"nbformat_minor": 2
}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -1,19 +1,19 @@
<!--
CO_OP_TRANSLATOR_METADATA:
{
"original_hash": "7336583e4630220c835335da640016db",
"translation_date": "2025-08-26T00:00:56+00:00",
"original_hash": "ba5d1eb353d20d3e7181066b3c424b99",
"translation_date": "2025-08-29T22:50:17+00:00",
"source_file": "lessons/3-NeuralNetworks/03-Perceptron/lab/README.md",
"language_code": "bg"
}
-->
# Класификация на множество класове с перцептрон
# Мултикласова класификация с перцептрон
Лабораторно упражнение от [AI for Beginners Curriculum](https://github.com/microsoft/ai-for-beginners).
## Задача
Използвайки кода, който разработихме в този урок за бинарна класификация на ръкописни цифри от MNIST, създайте класификатор за множество класове, който може да разпознава всяка цифра. Изчислете точността на класификацията върху тренировъчния и тестовия набор от данни и отпечатайте матрицата на объркване.
Използвайки кода, който разработихме в този урок за бинарна класификация на ръкописни цифри от MNIST, създайте мултикласов класификатор, който да може да разпознава всяка цифра. Изчислете точността на класификацията върху обучаващия и тестовия набор от данни и отпечатайте матрицата на объркване.
## Подсказки
@ -25,7 +25,9 @@ CO_OP_TRANSLATOR_METADATA:
## Начален ноутбук
Започнете лабораторното упражнение, като отворите [PerceptronMultiClass.ipynb](../../../../../../lessons/3-NeuralNetworks/03-Perceptron/lab/PerceptronMultiClass.ipynb)
Започнете упражнението, като отворите [PerceptronMultiClass.ipynb](PerceptronMultiClass.ipynb)
---
**Отказ от отговорност**:
Този документ е преведен с помощта на AI услуга за превод [Co-op Translator](https://github.com/Azure/co-op-translator). Въпреки че се стремим към точност, моля, имайте предвид, че автоматизираните преводи може да съдържат грешки или неточности. Оригиналният документ на неговия роден език трябва да се счита за авторитетен източник. За критична информация се препоръчва професионален човешки превод. Ние не носим отговорност за недоразумения или погрешни интерпретации, произтичащи от използването на този превод.

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,183 @@
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Класификация на цифри от MNIST с наша собствена рамка\n",
"\n",
"Лабораторно упражнение от [Учебна програма за начинаещи в изкуствения интелект](https://github.com/microsoft/ai-for-beginners).\n",
"\n",
"### Четене на набора от данни\n",
"\n",
"Този код изтегля набора от данни от хранилището в интернет. Можете също така ръчно да копирате набора от данни от директорията `/data` в хранилището на учебната програма за изкуствен интелект.\n"
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {
"tags": []
},
"outputs": [
{
"name": "stderr",
"output_type": "stream",
"text": [
" % Total % Received % Xferd Average Speed Time Time Time Current\n",
" Dload Upload Total Spent Left Speed\n",
"\n",
" 0 0 0 0 0 0 0 0 --:--:-- --:--:-- --:--:-- 0\n",
"100 9.9M 100 9.9M 0 0 9.9M 0 0:00:01 --:--:-- 0:00:01 15.8M\n"
]
}
],
"source": [
"!rm *.pkl\n",
"!wget https://raw.githubusercontent.com/microsoft/AI-For-Beginners/main/data/mnist.pkl.gz\n",
"!gzip -d mnist.pkl.gz"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {},
"outputs": [],
"source": [
"import pickle\n",
"with open('mnist.pkl','rb') as f:\n",
" MNIST = pickle.load(f)"
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {},
"outputs": [],
"source": [
"labels = MNIST['Train']['Labels']\n",
"data = MNIST['Train']['Features']"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Нека видим каква е формата на данните, които имаме:\n"
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"(42000, 784)"
]
},
"execution_count": 5,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"data.shape"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Разделяне на данните\n",
"\n",
"Ще използваме Scikit Learn, за да разделим данните на тренировъчен и тестов набор:\n"
]
},
{
"cell_type": "code",
"execution_count": 6,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Train samples: 33600, test samples: 8400\n"
]
}
],
"source": [
"from sklearn.model_selection import train_test_split\n",
"\n",
"features_train, features_test, labels_train, labels_test = train_test_split(data,labels,test_size=0.2)\n",
"\n",
"print(f\"Train samples: {len(features_train)}, test samples: {len(features_test)}\")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Инструкции\n",
"\n",
"1. Вземете кода на рамката от урока и го поставете в този notebook или (още по-добре) в отделен Python модул.\n",
"1. Дефинирайте и обучете еднослоен перцептрон, като наблюдавате точността на обучението и валидирането по време на обучението.\n",
"1. Опитайте се да разберете дали е настъпило пренасищане и коригирайте параметрите на слоя, за да подобрите точността.\n",
"1. Повторете предишните стъпки за двуслойни и трислойни перцептрони. Опитайте да експериментирате с различни функции за активация между слоевете.\n",
"1. Опитайте да отговорите на следните въпроси:\n",
" - Влияе ли функцията за активация между слоевете на производителността на мрежата?\n",
" - Нуждаем ли се от двуслойна или трислойна мрежа за тази задача?\n",
" - Срещнахте ли някакви проблеми при обучението на мрежата? Особено когато броят на слоевете се увеличи.\n",
" - Как се държат теглата на мрежата по време на обучението? Можете да начертаете максималната абсолютна стойност на теглата спрямо епохата, за да разберете връзката.\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": []
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"\n---\n\n**Отказ от отговорност**: \nТози документ е преведен с помощта на AI услуга за превод [Co-op Translator](https://github.com/Azure/co-op-translator). Въпреки че се стремим към точност, моля, имайте предвид, че автоматизираните преводи може да съдържат грешки или неточности. Оригиналният документ на неговия изходен език трябва да се счита за авторитетен източник. За критична информация се препоръчва професионален превод от човек. Ние не носим отговорност за каквито и да било недоразумения или погрешни интерпретации, произтичащи от използването на този превод.\n"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3.7.4 64-bit (conda)",
"metadata": {
"interpreter": {
"hash": "86193a1ab0ba47eac1c69c1756090baa3b420b3eea7d4aafab8b85f8b312f0c5"
}
},
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.9.5"
},
"orig_nbformat": 2,
"coopTranslator": {
"original_hash": "6fa055f484eb5d6bdf41166a356d3abf",
"translation_date": "2025-08-30T00:06:42+00:00",
"source_file": "lessons/3-NeuralNetworks/04-OwnFramework/lab/MyFW_MNIST.ipynb",
"language_code": "bg"
}
},
"nbformat": 4,
"nbformat_minor": 2
}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,108 @@
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Откриване на движение на дланта с помощта на оптичен поток\n",
"\n",
"Тази лабораторна работа е част от [Учебната програма за начинаещи в ИИ](http://aka.ms/ai-beginners).\n",
"\n",
"Разгледайте [това видео](../../../../../../lessons/4-ComputerVision/06-IntroCV/lab/palm-movement.mp4), в което дланта на човек се движи наляво/надясно/нагоре/надолу на стабилен фон.\n",
"\n",
"**Вашата цел** е да използвате оптичен поток, за да определите кои части от видеото съдържат движения нагоре/надолу/наляво/надясно.\n",
"\n",
"Започнете с извличане на кадри от видеото, както е описано в лекцията:\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# Code here"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Сега изчислете кадрите на плътния оптичен поток, както е описано в лекцията, и преобразувайте плътния оптичен поток в полярни координати:\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# Code here"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Създайте хистограма на посоките за всяка рамка на оптичния поток. Хистограмата показва колко вектори попадат в определен интервал и трябва да разделя различните посоки на движение в рамката.\n",
"\n",
"> Може също да искате да занулите всички вектори, чиято големина е под определен праг. Това ще премахне малки допълнителни движения във видеото, като например движения на очите и главата.\n",
"\n",
"Начертайте хистограмите за някои от рамките.\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# Code here"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Гледайки хистограмите, трябва да е доста лесно да се определи посоката на движение. Трябва да изберете тези колони, които съответстват на посоките нагоре/надолу/наляво/надясно и които са над определен праг.\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# Code here"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Поздравления! Ако сте изпълнили всички горепосочени стъпки, сте завършили лабораторията!\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"\n---\n\n**Отказ от отговорност**: \nТози документ е преведен с помощта на AI услуга за превод [Co-op Translator](https://github.com/Azure/co-op-translator). Въпреки че се стремим към точност, моля, имайте предвид, че автоматизираните преводи може да съдържат грешки или неточности. Оригиналният документ на неговия роден език трябва да се счита за авторитетен източник. За критична информация се препоръчва професионален човешки превод. Ние не носим отговорност за недоразумения или погрешни интерпретации, произтичащи от използването на този превод.\n"
]
}
],
"metadata": {
"language_info": {
"name": "python"
},
"orig_nbformat": 4,
"coopTranslator": {
"original_hash": "153d9e417e079bf62f8f693002d0deaf",
"translation_date": "2025-08-29T23:34:13+00:00",
"source_file": "lessons/4-ComputerVision/06-IntroCV/lab/MovementDetection.ipynb",
"language_code": "bg"
}
},
"nbformat": 4,
"nbformat_minor": 2
}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,577 @@
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Задача за класификация на текст\n",
"\n",
"Както споменахме, ще се фокусираме върху проста задача за класификация на текст, базирана на набора от данни **AG_NEWS**, която включва класифициране на заглавия на новини в една от 4 категории: Свят, Спорт, Бизнес и Наука/Технологии.\n",
"\n",
"## Наборът от данни\n",
"\n",
"Този набор от данни е вграден в модула [`torchtext`](https://github.com/pytorch/text), така че можем лесно да го достъпим.\n"
]
},
{
"cell_type": "code",
"execution_count": 1,
"metadata": {},
"outputs": [],
"source": [
"import torch\n",
"import torchtext\n",
"import os\n",
"import collections\n",
"os.makedirs('./data',exist_ok=True)\n",
"train_dataset, test_dataset = torchtext.datasets.AG_NEWS(root='./data')\n",
"classes = ['World', 'Sports', 'Business', 'Sci/Tech']"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Тук `train_dataset` и `test_dataset` съдържат колекции, които връщат двойки от етикет (номер на класа) и текст съответно, например:\n"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"(3,\n",
" \"Wall St. Bears Claw Back Into the Black (Reuters) Reuters - Short-sellers, Wall Street's dwindling\\\\band of ultra-cynics, are seeing green again.\")"
]
},
"execution_count": 2,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"list(train_dataset)[0]"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"И така, нека отпечатаме първите 10 нови заглавия от нашия набор от данни:\n"
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"**Sci/Tech** -> Wall St. Bears Claw Back Into the Black (Reuters) Reuters - Short-sellers, Wall Street's dwindling\\band of ultra-cynics, are seeing green again.\n",
"**Sci/Tech** -> Carlyle Looks Toward Commercial Aerospace (Reuters) Reuters - Private investment firm Carlyle Group,\\which has a reputation for making well-timed and occasionally\\controversial plays in the defense industry, has quietly placed\\its bets on another part of the market.\n",
"**Sci/Tech** -> Oil and Economy Cloud Stocks' Outlook (Reuters) Reuters - Soaring crude prices plus worries\\about the economy and the outlook for earnings are expected to\\hang over the stock market next week during the depth of the\\summer doldrums.\n",
"**Sci/Tech** -> Iraq Halts Oil Exports from Main Southern Pipeline (Reuters) Reuters - Authorities have halted oil export\\flows from the main pipeline in southern Iraq after\\intelligence showed a rebel militia could strike\\infrastructure, an oil official said on Saturday.\n",
"**Sci/Tech** -> Oil prices soar to all-time record, posing new menace to US economy (AFP) AFP - Tearaway world oil prices, toppling records and straining wallets, present a new economic menace barely three months before the US presidential elections.\n"
]
}
],
"source": [
"for i,x in zip(range(5),train_dataset):\n",
" print(f\"**{classes[x[0]]}** -> {x[1]}\")\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Тъй като наборите от данни са итератори, ако искаме да използваме данните многократно, трябва да ги преобразуваме в списък:\n"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {},
"outputs": [],
"source": [
"train_dataset, test_dataset = torchtext.datasets.AG_NEWS(root='./data')\n",
"train_dataset = list(train_dataset)\n",
"test_dataset = list(test_dataset)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Токенизация\n",
"\n",
"Сега трябва да преобразуваме текста в **числа**, които могат да бъдат представени като тензори. Ако искаме представяне на ниво думи, трябва да направим две неща:\n",
"* използваме **токенизатор**, за да разделим текста на **токени**\n",
"* изградим **речник** от тези токени.\n"
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"['he', 'said', 'hello']"
]
},
"execution_count": 4,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"tokenizer = torchtext.data.utils.get_tokenizer('basic_english')\n",
"tokenizer('He said: hello')"
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {},
"outputs": [],
"source": [
"counter = collections.Counter()\n",
"for (label, line) in train_dataset:\n",
" counter.update(tokenizer(line))\n",
"vocab = torchtext.vocab.vocab(counter, min_freq=1)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Използвайки речник, можем лесно да кодираме нашия токенизиран низ в набор от числа:\n"
]
},
{
"cell_type": "code",
"execution_count": 19,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Vocab size if 95810\n"
]
},
{
"data": {
"text/plain": [
"[599, 3279, 97, 1220, 329, 225, 7368]"
]
},
"execution_count": 19,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"vocab_size = len(vocab)\n",
"print(f\"Vocab size if {vocab_size}\")\n",
"\n",
"stoi = vocab.get_stoi() # dict to convert tokens to indices\n",
"\n",
"def encode(x):\n",
" return [stoi[s] for s in tokenizer(x)]\n",
"\n",
"encode('I love to play with my words')"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Представяне на текст чрез \"Чанта с думи\"\n",
"\n",
"Тъй като думите носят значение, понякога можем да разберем смисъла на текста, като просто разгледаме отделните думи, без да обръщаме внимание на техния ред в изречението. Например, при класифициране на новини, думи като *време*, *сняг* вероятно ще насочат към *прогноза за времето*, докато думи като *акции*, *долар* биха се отнесли към *финансови новини*.\n",
"\n",
"**Чанта с думи** (BoW) е най-често използваното традиционно векторно представяне. Всяка дума е свързана с индекс във вектора, а елементът на вектора съдържа броя на срещанията на дадена дума в конкретен документ.\n",
"\n",
"![Изображение, показващо как представянето чрез \"чанта с думи\" се съхранява в паметта.](../../../../../translated_images/bag-of-words-example.606fc1738f1d7ba98a9d693e3bcd706c6e83fa7bf8221e6e90d1a206d82f2ea4.bg.png) \n",
"\n",
"> **Note**: Можете също да мислите за BoW като за сума от всички едноразрядно кодирани вектори за отделните думи в текста.\n",
"\n",
"По-долу е даден пример как да генерирате представяне чрез \"чанта с думи\", използвайки библиотеката Scikit Learn на Python:\n"
]
},
{
"cell_type": "code",
"execution_count": 7,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"array([[1, 1, 0, 2, 0, 0, 0, 0, 0]], dtype=int64)"
]
},
"execution_count": 7,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"from sklearn.feature_extraction.text import CountVectorizer\n",
"vectorizer = CountVectorizer()\n",
"corpus = [\n",
" 'I like hot dogs.',\n",
" 'The dog ran fast.',\n",
" 'Its hot outside.',\n",
" ]\n",
"vectorizer.fit_transform(corpus)\n",
"vectorizer.transform(['My dog likes hot dogs on a hot day.']).toarray()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"За да изчислим bag-of-words вектор от векторното представяне на нашия AG_NEWS набор от данни, можем да използваме следната функция:\n"
]
},
{
"cell_type": "code",
"execution_count": 20,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"tensor([2., 1., 2., ..., 0., 0., 0.])\n"
]
}
],
"source": [
"vocab_size = len(vocab)\n",
"\n",
"def to_bow(text,bow_vocab_size=vocab_size):\n",
" res = torch.zeros(bow_vocab_size,dtype=torch.float32)\n",
" for i in encode(text):\n",
" if i<bow_vocab_size:\n",
" res[i] += 1\n",
" return res\n",
"\n",
"print(to_bow(train_dataset[0][1]))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"> **Забележка:** Тук използваме глобалната променлива `vocab_size`, за да зададем стандартния размер на речника. Тъй като често размерът на речника е доста голям, можем да го ограничим до най-често срещаните думи. Опитайте да намалите стойността на `vocab_size` и да изпълните кода по-долу, за да видите как това влияе на точността. Трябва да очаквате известно намаление на точността, но не драстично, за сметка на по-висока производителност.\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Обучение на класификатор с метод Bag-of-Words\n",
"\n",
"Сега, след като научихме как да изградим представяне на текста чрез Bag-of-Words, нека обучим класификатор върху него. Първо, трябва да преобразуваме нашия набор от данни за обучение така, че всички позиционни векторни представяния да бъдат преобразувани в представяне чрез Bag-of-Words. Това може да се постигне, като предадем функцията `bowify` като параметър `collate_fn` на стандартния torch `DataLoader`:\n"
]
},
{
"cell_type": "code",
"execution_count": 21,
"metadata": {},
"outputs": [],
"source": [
"from torch.utils.data import DataLoader\n",
"import numpy as np \n",
"\n",
"# this collate function gets list of batch_size tuples, and needs to \n",
"# return a pair of label-feature tensors for the whole minibatch\n",
"def bowify(b):\n",
" return (\n",
" torch.LongTensor([t[0]-1 for t in b]),\n",
" torch.stack([to_bow(t[1]) for t in b])\n",
" )\n",
"\n",
"train_loader = DataLoader(train_dataset, batch_size=16, collate_fn=bowify, shuffle=True)\n",
"test_loader = DataLoader(test_dataset, batch_size=16, collate_fn=bowify, shuffle=True)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Сега нека дефинираме прост класификаторен невронен модел, който съдържа един линеен слой. Размерът на входния вектор е равен на `vocab_size`, а размерът на изхода съответства на броя на класовете (4). Тъй като решаваме задача за класификация, крайната активационна функция е `LogSoftmax()`.\n"
]
},
{
"cell_type": "code",
"execution_count": 22,
"metadata": {},
"outputs": [],
"source": [
"net = torch.nn.Sequential(torch.nn.Linear(vocab_size,4),torch.nn.LogSoftmax(dim=1))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Сега ще дефинираме стандартен цикъл за обучение в PyTorch. Тъй като нашият набор от данни е доста голям, за целите на обучението ще тренираме само за една епоха, а понякога дори за по-малко от една епоха (параметърът `epoch_size` ни позволява да ограничим обучението). Също така ще докладваме натрупаната точност на обучението по време на тренировката; честотата на докладване се определя чрез параметъра `report_freq`.\n"
]
},
{
"cell_type": "code",
"execution_count": 24,
"metadata": {},
"outputs": [],
"source": [
"def train_epoch(net,dataloader,lr=0.01,optimizer=None,loss_fn = torch.nn.NLLLoss(),epoch_size=None, report_freq=200):\n",
" optimizer = optimizer or torch.optim.Adam(net.parameters(),lr=lr)\n",
" net.train()\n",
" total_loss,acc,count,i = 0,0,0,0\n",
" for labels,features in dataloader:\n",
" optimizer.zero_grad()\n",
" out = net(features)\n",
" loss = loss_fn(out,labels) #cross_entropy(out,labels)\n",
" loss.backward()\n",
" optimizer.step()\n",
" total_loss+=loss\n",
" _,predicted = torch.max(out,1)\n",
" acc+=(predicted==labels).sum()\n",
" count+=len(labels)\n",
" i+=1\n",
" if i%report_freq==0:\n",
" print(f\"{count}: acc={acc.item()/count}\")\n",
" if epoch_size and count>epoch_size:\n",
" break\n",
" return total_loss.item()/count, acc.item()/count"
]
},
{
"cell_type": "code",
"execution_count": 25,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"3200: acc=0.8028125\n",
"6400: acc=0.8371875\n",
"9600: acc=0.8534375\n",
"12800: acc=0.85765625\n"
]
},
{
"data": {
"text/plain": [
"(0.026090790722161722, 0.8620069296375267)"
]
},
"execution_count": 25,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"train_epoch(net,train_loader,epoch_size=15000)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Биграми, триграми и N-грами\n",
"\n",
"Едно от ограниченията на подхода с чанта от думи е, че някои думи са част от изрази, състоящи се от няколко думи. Например, думата „hot dog“ има напълно различно значение от думите „hot“ и „dog“ в други контексти. Ако винаги представяме думите „hot“ и „dog“ със същите вектори, това може да обърка модела ни.\n",
"\n",
"За да се справим с това, често се използват **представяния с N-грам** в методите за класификация на документи, където честотата на всяка дума, двойка думи или тройка думи е полезна характеристика за обучение на класификатори. При представяне с биграми, например, ще добавим всички двойки думи към речника, в допълнение към оригиналните думи.\n",
"\n",
"По-долу е даден пример за това как да генерираме представяне с чанта от думи с биграми, използвайки Scikit Learn:\n"
]
},
{
"cell_type": "code",
"execution_count": 26,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Vocabulary:\n",
" {'i': 7, 'like': 11, 'hot': 4, 'dogs': 2, 'i like': 8, 'like hot': 12, 'hot dogs': 5, 'the': 16, 'dog': 0, 'ran': 14, 'fast': 3, 'the dog': 17, 'dog ran': 1, 'ran fast': 15, 'its': 9, 'outside': 13, 'its hot': 10, 'hot outside': 6}\n"
]
},
{
"data": {
"text/plain": [
"array([[1, 0, 1, 0, 2, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]],\n",
" dtype=int64)"
]
},
"execution_count": 26,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"bigram_vectorizer = CountVectorizer(ngram_range=(1, 2), token_pattern=r'\\b\\w+\\b', min_df=1)\n",
"corpus = [\n",
" 'I like hot dogs.',\n",
" 'The dog ran fast.',\n",
" 'Its hot outside.',\n",
" ]\n",
"bigram_vectorizer.fit_transform(corpus)\n",
"print(\"Vocabulary:\\n\",bigram_vectorizer.vocabulary_)\n",
"bigram_vectorizer.transform(['My dog likes hot dogs on a hot day.']).toarray()\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Основният недостатък на подхода с N-грам е, че размерът на речника започва да нараства изключително бързо. На практика е необходимо да комбинираме представянето с N-грам с някои техники за намаляване на размерността, като например *вграждания*, които ще обсъдим в следващия модул.\n",
"\n",
"За да използваме представянето с N-грам в нашия **AG News** набор от данни, трябва да изградим специален речник за n-грам:\n"
]
},
{
"cell_type": "code",
"execution_count": 27,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Bigram vocabulary length = 1308842\n"
]
}
],
"source": [
"counter = collections.Counter()\n",
"for (label, line) in train_dataset:\n",
" l = tokenizer(line)\n",
" counter.update(torchtext.data.utils.ngrams_iterator(l,ngrams=2))\n",
" \n",
"bi_vocab = torchtext.vocab.vocab(counter, min_freq=1)\n",
"\n",
"print(\"Bigram vocabulary length = \",len(bi_vocab))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Можем да използваме същия код като горния, за да обучим класификатора, но това би било много неефективно по отношение на паметта. В следващия модул ще обучим биграм класификатор, използвайки вграждания.\n",
"\n",
"> **Note:** Можете да оставите само онези n-грамове, които се срещат в текста повече от определен брой пъти. Това ще гарантира, че рядко срещаните биграми ще бъдат пропуснати и значително ще намали размерността. За да направите това, задайте параметъра `min_freq` на по-висока стойност и наблюдавайте как се променя дължината на речника.\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Честота на термина и обратна честота на документа (TF-IDF)\n",
"\n",
"В представянето на BoW (Bag of Words), срещанията на думите се претеглят равномерно, независимо от самата дума. Въпреки това е очевидно, че често срещани думи като *а*, *в* и т.н. са много по-малко важни за класификацията в сравнение със специализираните термини. Всъщност, в повечето задачи по обработка на естествен език (NLP) някои думи са по-релевантни от други.\n",
"\n",
"**TF-IDF** означава **честота на термина обратна честота на документа**. Това е вариация на модела \"чанта с думи\" (BoW), при която вместо бинарна стойност 0/1, указваща наличието на дума в даден документ, се използва стойност с плаваща запетая, която е свързана с честотата на срещане на думата в корпуса.\n",
"\n",
"По-формално, теглото $w_{ij}$ на дума $i$ в документ $j$ се дефинира като:\n",
"$$\n",
"w_{ij} = tf_{ij}\\times\\log({N\\over df_i})\n",
"$$\n",
"където:\n",
"* $tf_{ij}$ е броят на срещанията на $i$ в $j$, т.е. стойността на BoW, която видяхме по-рано\n",
"* $N$ е броят на документите в колекцията\n",
"* $df_i$ е броят на документите, съдържащи думата $i$ в цялата колекция\n",
"\n",
"Стойността на TF-IDF $w_{ij}$ нараства пропорционално на броя пъти, в които една дума се появява в даден документ, и се коригира спрямо броя на документите в корпуса, които съдържат тази дума. Това помага да се компенсира фактът, че някои думи се срещат по-често от други. Например, ако дадена дума се появява *във всеки* документ от колекцията, тогава $df_i=N$, и $w_{ij}=0$, като тези термини ще бъдат напълно игнорирани.\n",
"\n",
"Можете лесно да създадете векторизация на текст с TF-IDF, използвайки Scikit Learn:\n"
]
},
{
"cell_type": "code",
"execution_count": 28,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"array([[0.43381609, 0. , 0.43381609, 0. , 0.65985664,\n",
" 0.43381609, 0. , 0. , 0. , 0. ,\n",
" 0. , 0. , 0. , 0. , 0. ,\n",
" 0. ]])"
]
},
"execution_count": 28,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"from sklearn.feature_extraction.text import TfidfVectorizer\n",
"vectorizer = TfidfVectorizer(ngram_range=(1,2))\n",
"vectorizer.fit_transform(corpus)\n",
"vectorizer.transform(['My dog likes hot dogs on a hot day.']).toarray()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Заключение\n",
"\n",
"Въпреки че представянията чрез TF-IDF предоставят тегло на честотата за различни думи, те не могат да представят значението или реда. Както известният лингвист Дж. Р. Фърт каза през 1935 г., „Пълното значение на една дума винаги е контекстуално, и никакво изследване на значението извън контекста не може да бъде взето на сериозно.“. По-късно в курса ще научим как да улавяме контекстуална информация от текст чрез моделиране на езика.\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"\n---\n\n**Отказ от отговорност**: \nТози документ е преведен с помощта на AI услуга за превод [Co-op Translator](https://github.com/Azure/co-op-translator). Въпреки че се стремим към точност, моля, имайте предвид, че автоматизираните преводи може да съдържат грешки или неточности. Оригиналният документ на неговия роден език трябва да се счита за авторитетен източник. За критична информация се препоръчва професионален човешки превод. Ние не носим отговорност за недоразумения или погрешни интерпретации, произтичащи от използването на този превод.\n"
]
}
],
"metadata": {
"interpreter": {
"hash": "16af2a8bbb083ea23e5e41c7f5787656b2ce26968575d8763f2c4b17f9cd711f"
},
"kernelspec": {
"display_name": "Python 3.8.12 ('py38')",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.8.12"
},
"coopTranslator": {
"original_hash": "7b9040985e748e4e2d4c689892456ad7",
"translation_date": "2025-08-30T01:13:03+00:00",
"source_file": "lessons/5-NLP/13-TextRep/TextRepresentationPyTorch.ipynb",
"language_code": "bg"
}
},
"nbformat": 4,
"nbformat_minor": 2
}

View File

@ -0,0 +1,647 @@
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Задача за класификация на текст\n",
"\n",
"В този модул ще започнем с проста задача за класификация на текст, базирана на **[AG_NEWS](http://www.di.unipi.it/~gulli/AG_corpus_of_news_articles.html)** набора от данни: ще класифицираме заглавия на новини в една от 4 категории: Свят, Спорт, Бизнес и Наука/Технологии.\n",
"\n",
"## Наборът от данни\n",
"\n",
"За да заредим набора от данни, ще използваме **[TensorFlow Datasets](https://www.tensorflow.org/datasets)** API.\n"
]
},
{
"cell_type": "code",
"execution_count": 1,
"metadata": {},
"outputs": [],
"source": [
"import tensorflow as tf\n",
"from tensorflow import keras\n",
"import tensorflow_datasets as tfds\n",
"\n",
"# In this tutorial, we will be training a lot of models. In order to use GPU memory cautiously,\n",
"# we will set tensorflow option to grow GPU memory allocation when required.\n",
"physical_devices = tf.config.list_physical_devices('GPU') \n",
"if len(physical_devices)>0:\n",
" tf.config.experimental.set_memory_growth(physical_devices[0], True)\n",
"\n",
"dataset = tfds.load('ag_news_subset')"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Сега можем да достъпим тренировъчната и тестовата част на набора от данни, като използваме `dataset['train']` и `dataset['test']` съответно:\n"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Length of train dataset = 120000\n",
"Length of test dataset = 7600\n"
]
}
],
"source": [
"ds_train = dataset['train']\n",
"ds_test = dataset['test']\n",
"\n",
"print(f\"Length of train dataset = {len(ds_train)}\")\n",
"print(f\"Length of test dataset = {len(ds_test)}\")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Нека отпечатаме първите 10 нови заглавия от нашия набор от данни:\n"
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"3 (Sci/Tech) -> b'AMD Debuts Dual-Core Opteron Processor' b'AMD #39;s new dual-core Opteron chip is designed mainly for corporate computing applications, including databases, Web services, and financial transactions.'\n",
"1 (Sports) -> b\"Wood's Suspension Upheld (Reuters)\" b'Reuters - Major League Baseball\\\\Monday announced a decision on the appeal filed by Chicago Cubs\\\\pitcher Kerry Wood regarding a suspension stemming from an\\\\incident earlier this season.'\n",
"2 (Business) -> b'Bush reform may have blue states seeing red' b'President Bush #39;s quot;revenue-neutral quot; tax reform needs losers to balance its winners, and people claiming the federal deduction for state and local taxes may be in administration planners #39; sights, news reports say.'\n",
"3 (Sci/Tech) -> b\"'Halt science decline in schools'\" b'Britain will run out of leading scientists unless science education is improved, says Professor Colin Pillinger.'\n",
"1 (Sports) -> b'Gerrard leaves practice' b'London, England (Sports Network) - England midfielder Steven Gerrard injured his groin late in Thursday #39;s training session, but is hopeful he will be ready for Saturday #39;s World Cup qualifier against Austria.'\n"
]
}
],
"source": [
"classes = ['World', 'Sports', 'Business', 'Sci/Tech']\n",
"\n",
"for i,x in zip(range(5),ds_train):\n",
" print(f\"{x['label']} ({classes[x['label']]}) -> {x['title']} {x['description']}\")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Векторизация на текст\n",
"\n",
"Сега трябва да преобразуваме текста в **числа**, които могат да бъдат представени като тензори. Ако искаме представяне на ниво думи, трябва да направим две неща:\n",
"\n",
"* Използване на **токенизатор**, за да разделим текста на **токени**.\n",
"* Създаване на **речник** от тези токени.\n",
"\n",
"### Ограничаване на размера на речника\n",
"\n",
"В примера с набора от данни AG News, размерът на речника е доста голям — повече от 100 хиляди думи. По принцип, не ни трябват думи, които рядко се срещат в текста — само няколко изречения ще ги съдържат, и моделът няма да се научи от тях. Затова има логика да ограничим размера на речника до по-малко число, като подадем аргумент на конструктора на векторизатора:\n",
"\n",
"И двата тези етапа могат да бъдат обработени с помощта на слоя **TextVectorization**. Нека създадем обекта на векторизатора и след това извикаме метода `adapt`, за да преминем през целия текст и да изградим речник:\n"
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {},
"outputs": [],
"source": [
"vocab_size = 50000\n",
"vectorizer = keras.layers.experimental.preprocessing.TextVectorization(max_tokens=vocab_size)\n",
"vectorizer.adapt(ds_train.take(500).map(lambda x: x['title']+' '+x['description']))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"> **Забележка**: Използваме само подмножество от целия набор от данни, за да изградим речник. Правим това, за да ускорим времето за изпълнение и да не ви караме да чакате. Въпреки това, поемаме риска, че някои от думите от целия набор от данни няма да бъдат включени в речника и ще бъдат игнорирани по време на обучението. Следователно, използването на целия размер на речника и преминаването през целия набор от данни по време на `adapt` би трябвало да увеличи крайната точност, но не значително.\n",
"\n",
"Сега можем да достъпим действителния речник:\n"
]
},
{
"cell_type": "code",
"execution_count": 6,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"['', '[UNK]', 'the', 'to', 'a', 'in', 'of', 'and', 'on', 'for']\n",
"Length of vocabulary: 5335\n"
]
}
],
"source": [
"vocab = vectorizer.get_vocabulary()\n",
"vocab_size = len(vocab)\n",
"print(vocab[:10])\n",
"print(f\"Length of vocabulary: {vocab_size}\")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Използвайки векторизатора, можем лесно да кодираме всеки текст в набор от числа:\n"
]
},
{
"cell_type": "code",
"execution_count": 7,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"<tf.Tensor: shape=(7,), dtype=int64, numpy=array([ 112, 3695, 3, 304, 11, 1041, 1], dtype=int64)>"
]
},
"execution_count": 7,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"vectorizer('I love to play with my words')"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Представяне на текст чрез \"чанта с думи\"\n",
"\n",
"Тъй като думите носят значение, понякога можем да разберем смисъла на даден текст, като просто разгледаме отделните думи, без да обръщаме внимание на техния ред в изречението. Например, при класифициране на новини, думи като *време* и *сняг* вероятно ще насочат към *прогноза за времето*, докато думи като *акции* и *долар* биха се отнасяли към *финансови новини*.\n",
"\n",
"**Чанта с думи** (BoW) е най-простото за разбиране традиционно представяне на вектор. Всяка дума е свързана с индекс във вектора, а елементът на вектора съдържа броя на срещанията на всяка дума в даден документ.\n",
"\n",
"![Изображение, показващо как представянето чрез чанта с думи се съхранява в паметта.](../../../../../translated_images/bag-of-words-example.606fc1738f1d7ba98a9d693e3bcd706c6e83fa7bf8221e6e90d1a206d82f2ea4.bg.png) \n",
"\n",
"> **Note**: Можете също да мислите за BoW като сума от всички едно-горещо-кодирани вектори за отделните думи в текста.\n",
"\n",
"По-долу е даден пример за това как да генерирате представяне чрез чанта с думи, използвайки библиотеката Scikit Learn на Python:\n"
]
},
{
"cell_type": "code",
"execution_count": 8,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"array([[1, 1, 0, 2, 0, 0, 0, 0, 0]], dtype=int64)"
]
},
"execution_count": 8,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"from sklearn.feature_extraction.text import CountVectorizer\n",
"sc_vectorizer = CountVectorizer()\n",
"corpus = [\n",
" 'I like hot dogs.',\n",
" 'The dog ran fast.',\n",
" 'Its hot outside.',\n",
" ]\n",
"sc_vectorizer.fit_transform(corpus)\n",
"sc_vectorizer.transform(['My dog likes hot dogs on a hot day.']).toarray()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Можем също да използваме Keras векторизатора, който дефинирахме по-горе, като преобразуваме всяко число на дума в one-hot кодиране и събираме всички тези вектори.\n"
]
},
{
"cell_type": "code",
"execution_count": 9,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"array([0., 5., 0., ..., 0., 0., 0.], dtype=float32)"
]
},
"execution_count": 9,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"def to_bow(text):\n",
" return tf.reduce_sum(tf.one_hot(vectorizer(text),vocab_size),axis=0)\n",
"\n",
"to_bow('My dog likes hot dogs on a hot day.').numpy()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"> **Забележка**: Може да се изненадате, че резултатът се различава от предишния пример. Причината е, че в примера с Keras дължината на вектора съответства на размера на речника, който е създаден от целия набор данни AG News, докато в примера със Scikit Learn създадохме речника от примерния текст в движение.\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Обучение на класификатора BoW\n",
"\n",
"Сега, след като научихме как да създадем представянето на текста като \"чанта с думи\", нека обучим класификатор, който го използва. Първо, трябва да преобразуваме нашия набор от данни в представяне като \"чанта с думи\". Това може да се постигне чрез използване на функцията `map` по следния начин:\n"
]
},
{
"cell_type": "code",
"execution_count": 11,
"metadata": {},
"outputs": [],
"source": [
"batch_size = 128\n",
"\n",
"ds_train_bow = ds_train.map(lambda x: (to_bow(x['title']+x['description']),x['label'])).batch(batch_size)\n",
"ds_test_bow = ds_test.map(lambda x: (to_bow(x['title']+x['description']),x['label'])).batch(batch_size)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Сега нека дефинираме прост класификаторен невронен модел, който съдържа един линеен слой. Размерът на входа е `vocab_size`, а размерът на изхода съответства на броя на класовете (4). Тъй като решаваме задача за класификация, крайната активационна функция е **softmax**:\n"
]
},
{
"cell_type": "code",
"execution_count": 12,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"938/938 [==============================] - 66s 70ms/step - loss: 0.6144 - acc: 0.8427 - val_loss: 0.4416 - val_acc: 0.8697\n"
]
},
{
"data": {
"text/plain": [
"<keras.callbacks.History at 0x20c70a947f0>"
]
},
"execution_count": 12,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"model = keras.models.Sequential([\n",
" keras.layers.Dense(4,activation='softmax',input_shape=(vocab_size,))\n",
"])\n",
"model.compile(loss='sparse_categorical_crossentropy',optimizer='adam',metrics=['acc'])\n",
"model.fit(ds_train_bow,validation_data=ds_test_bow)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Тъй като имаме 4 класа, точност над 80% е добър резултат.\n",
"\n",
"## Обучение на класификатор като една мрежа\n",
"\n",
"Тъй като векторизаторът също е слой на Keras, можем да дефинираме мрежа, която го включва, и да я обучим от край до край. По този начин не е необходимо да векторизираме набора от данни с помощта на `map`, а можем просто да подадем оригиналния набор от данни към входа на мрежата.\n",
"\n",
"> **Note**: Все пак ще трябва да приложим `map` към нашия набор от данни, за да преобразуваме полетата от речници (като `title`, `description` и `label`) в кортежи. Въпреки това, когато зареждаме данни от диска, можем да изградим набор от данни с необходимата структура още от самото начало.\n"
]
},
{
"cell_type": "code",
"execution_count": 13,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Model: \"model\"\n",
"_________________________________________________________________\n",
" Layer (type) Output Shape Param # \n",
"=================================================================\n",
" input_1 (InputLayer) [(None, 1)] 0 \n",
" \n",
" text_vectorization (TextVec (None, None) 0 \n",
" torization) \n",
" \n",
" tf.one_hot (TFOpLambda) (None, None, 5335) 0 \n",
" \n",
" tf.math.reduce_sum (TFOpLam (None, 5335) 0 \n",
" bda) \n",
" \n",
" dense_2 (Dense) (None, 4) 21344 \n",
" \n",
"=================================================================\n",
"Total params: 21,344\n",
"Trainable params: 21,344\n",
"Non-trainable params: 0\n",
"_________________________________________________________________\n",
"938/938 [==============================] - 73s 77ms/step - loss: 0.6057 - acc: 0.8414 - val_loss: 0.4202 - val_acc: 0.8736\n"
]
},
{
"data": {
"text/plain": [
"<keras.callbacks.History at 0x20c721521f0>"
]
},
"execution_count": 13,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"def extract_text(x):\n",
" return x['title']+' '+x['description']\n",
"\n",
"def tupelize(x):\n",
" return (extract_text(x),x['label'])\n",
"\n",
"inp = keras.Input(shape=(1,),dtype=tf.string)\n",
"x = vectorizer(inp)\n",
"x = tf.reduce_sum(tf.one_hot(x,vocab_size),axis=1)\n",
"out = keras.layers.Dense(4,activation='softmax')(x)\n",
"model = keras.models.Model(inp,out)\n",
"model.summary()\n",
"\n",
"model.compile(loss='sparse_categorical_crossentropy',optimizer='adam',metrics=['acc'])\n",
"model.fit(ds_train.map(tupelize).batch(batch_size),validation_data=ds_test.map(tupelize).batch(batch_size))\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Биграми, триграми и n-грамове\n",
"\n",
"Едно от ограниченията на подхода \"чанта с думи\" е, че някои думи са част от изрази с няколко думи. Например, думата „hot dog“ има напълно различно значение от думите „hot“ и „dog“ в други контексти. Ако винаги представяме думите „hot“ и „dog“ с едни и същи вектори, това може да обърка нашия модел.\n",
"\n",
"За да се справим с това, често се използват **n-грамови представяния** в методите за класификация на документи, където честотата на всяка дума, дву-дума или три-дума е полезна характеристика за обучение на класификатори. При биграмовите представяния, например, ще добавим всички двойки думи към речника, в допълнение към оригиналните думи.\n",
"\n",
"По-долу е даден пример как да се генерира биграмово представяне на чанта с думи, използвайки Scikit Learn:\n"
]
},
{
"cell_type": "code",
"execution_count": 14,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Vocabulary:\n",
" {'i': 7, 'like': 11, 'hot': 4, 'dogs': 2, 'i like': 8, 'like hot': 12, 'hot dogs': 5, 'the': 16, 'dog': 0, 'ran': 14, 'fast': 3, 'the dog': 17, 'dog ran': 1, 'ran fast': 15, 'its': 9, 'outside': 13, 'its hot': 10, 'hot outside': 6}\n"
]
},
{
"data": {
"text/plain": [
"array([[1, 0, 1, 0, 2, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]],\n",
" dtype=int64)"
]
},
"execution_count": 14,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"bigram_vectorizer = CountVectorizer(ngram_range=(1, 2), token_pattern=r'\\b\\w+\\b', min_df=1)\n",
"corpus = [\n",
" 'I like hot dogs.',\n",
" 'The dog ran fast.',\n",
" 'Its hot outside.',\n",
" ]\n",
"bigram_vectorizer.fit_transform(corpus)\n",
"print(\"Vocabulary:\\n\",bigram_vectorizer.vocabulary_)\n",
"bigram_vectorizer.transform(['My dog likes hot dogs on a hot day.']).toarray()\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Основният недостатък на подхода с n-грам е, че размерът на речника започва да нараства изключително бързо. На практика е необходимо да комбинираме представянето с n-грам с техника за намаляване на размерността, като например *вграждания* (embeddings), които ще обсъдим в следващия модул.\n",
"\n",
"За да използваме представяне с n-грам в нашия **AG News** набор от данни, трябва да подадем параметъра `ngrams` на конструктора на `TextVectorization`. Дължината на речника за биграми е **значително по-голяма** в нашия случай тя е над 1.3 милиона токена! Затова има смисъл да ограничим и токените за биграми до някакъв разумен брой.\n",
"\n",
"Можем да използваме същия код като по-горе, за да обучим класификатора, но това би било много неефективно по отношение на паметта. В следващия модул ще обучим класификатор за биграми, използвайки вграждания. Междувременно можете да експериментирате с обучението на класификатор за биграми в този ноутбук и да видите дали можете да постигнете по-висока точност.\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Автоматично изчисляване на BoW вектори\n",
"\n",
"В примера по-горе изчислихме BoW вектори ръчно, като събрахме едноразрядните кодировки на отделните думи. Въпреки това, последната версия на TensorFlow ни позволява автоматично да изчисляваме BoW вектори, като предадем параметъра `output_mode='count` на конструктора на векторизатора. Това значително улеснява дефинирането и обучението на нашия модел:\n"
]
},
{
"cell_type": "code",
"execution_count": 15,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Training vectorizer\n",
"938/938 [==============================] - 7s 7ms/step - loss: 0.5929 - acc: 0.8486 - val_loss: 0.4168 - val_acc: 0.8772\n"
]
},
{
"data": {
"text/plain": [
"<keras.callbacks.History at 0x20c725217c0>"
]
},
"execution_count": 15,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"model = keras.models.Sequential([\n",
" keras.layers.experimental.preprocessing.TextVectorization(max_tokens=vocab_size,output_mode='count'),\n",
" keras.layers.Dense(4,input_shape=(vocab_size,), activation='softmax')\n",
"])\n",
"print(\"Training vectorizer\")\n",
"model.layers[0].adapt(ds_train.take(500).map(extract_text))\n",
"model.compile(loss='sparse_categorical_crossentropy',optimizer='adam',metrics=['acc'])\n",
"model.fit(ds_train.map(tupelize).batch(batch_size),validation_data=ds_test.map(tupelize).batch(batch_size))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Честота на термина - обратна честота на документа (TF-IDF)\n",
"\n",
"В представянето на BoW, появите на думите се претеглят с една и съща техника, независимо от самата дума. Въпреки това е ясно, че често срещани думи като *a* и *in* са много по-малко важни за класификацията в сравнение със специализирани термини. В повечето задачи в обработката на естествен език някои думи са по-релевантни от други.\n",
"\n",
"**TF-IDF** означава **честота на термина - обратна честота на документа**. Това е вариация на bag-of-words, при която вместо бинарна стойност 0/1, указваща появата на дума в документ, се използва стойност с плаваща запетая, която е свързана с честотата на появата на думата в корпуса.\n",
"\n",
"По-формално, теглото $w_{ij}$ на дума $i$ в документ $j$ се дефинира като:\n",
"$$\n",
"w_{ij} = tf_{ij}\\times\\log({N\\over df_i})\n",
"$$\n",
"където\n",
"* $tf_{ij}$ е броят на появите на $i$ в $j$, т.е. стойността на BoW, която видяхме по-рано\n",
"* $N$ е броят на документите в колекцията\n",
"* $df_i$ е броят на документите, съдържащи думата $i$ в цялата колекция\n",
"\n",
"Стойността на TF-IDF $w_{ij}$ нараства пропорционално на броя пъти, в които една дума се появява в документ, и се компенсира от броя на документите в корпуса, които съдържат думата. Това помага да се коригира фактът, че някои думи се появяват по-често от други. Например, ако думата се появява във *всеки* документ в колекцията, $df_i=N$, и $w_{ij}=0$, и тези термини ще бъдат напълно игнорирани.\n",
"\n",
"Можете лесно да създадете векторизация на текст с TF-IDF, използвайки Scikit Learn:\n"
]
},
{
"cell_type": "code",
"execution_count": 16,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"array([[0.43381609, 0. , 0.43381609, 0. , 0.65985664,\n",
" 0.43381609, 0. , 0. , 0. , 0. ,\n",
" 0. , 0. , 0. , 0. , 0. ,\n",
" 0. ]])"
]
},
"execution_count": 16,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"from sklearn.feature_extraction.text import TfidfVectorizer\n",
"vectorizer = TfidfVectorizer(ngram_range=(1,2))\n",
"vectorizer.fit_transform(corpus)\n",
"vectorizer.transform(['My dog likes hot dogs on a hot day.']).toarray()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"В Keras слоят `TextVectorization` може автоматично да изчислява TF-IDF честоти, като се зададе параметърът `output_mode='tf-idf'`. Нека повторим кода, който използвахме по-горе, за да видим дали използването на TF-IDF увеличава точността:\n"
]
},
{
"cell_type": "code",
"execution_count": 17,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Training vectorizer\n",
"938/938 [==============================] - 12s 12ms/step - loss: 0.4197 - acc: 0.8662 - val_loss: 0.3432 - val_acc: 0.8849\n"
]
},
{
"data": {
"text/plain": [
"<keras.callbacks.History at 0x20c729dfd30>"
]
},
"execution_count": 17,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"model = keras.models.Sequential([\n",
" keras.layers.experimental.preprocessing.TextVectorization(max_tokens=vocab_size,output_mode='tf-idf'),\n",
" keras.layers.Dense(4,input_shape=(vocab_size,), activation='softmax')\n",
"])\n",
"print(\"Training vectorizer\")\n",
"model.layers[0].adapt(ds_train.take(500).map(extract_text))\n",
"model.compile(loss='sparse_categorical_crossentropy',optimizer='adam',metrics=['acc'])\n",
"model.fit(ds_train.map(tupelize).batch(batch_size),validation_data=ds_test.map(tupelize).batch(batch_size))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Заключение\n",
"\n",
"Въпреки че представянията с TF-IDF предоставят тегла на честотата за различни думи, те не могат да представят значението или реда. Както известният лингвист Дж. Р. Фърт каза през 1935 г., \"Пълното значение на една дума винаги е контекстуално, и никакво изследване на значението извън контекста не може да бъде взето на сериозно.\" По-късно в курса ще научим как да улавяме контекстуална информация от текст чрез моделиране на езика.\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"\n---\n\n**Отказ от отговорност**: \nТози документ е преведен с помощта на AI услуга за превод [Co-op Translator](https://github.com/Azure/co-op-translator). Въпреки че се стремим към точност, моля, имайте предвид, че автоматизираните преводи може да съдържат грешки или неточности. Оригиналният документ на неговия роден език трябва да се счита за авторитетен източник. За критична информация се препоръчва професионален човешки превод. Ние не носим отговорност за недоразумения или погрешни интерпретации, произтичащи от използването на този превод.\n"
]
}
],
"metadata": {
"interpreter": {
"hash": "0cb620c6d4b9f7a635928804c26cf22403d89d98d79684e4529119355ee6d5a5"
},
"kernel_info": {
"name": "conda-env-py37_tensorflow-py"
},
"kernelspec": {
"display_name": "py37_tensorflow",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.8.12"
},
"nteract": {
"version": "nteract-front-end@1.0.0"
},
"coopTranslator": {
"original_hash": "19b43951d55b377a76209c24c1f017e4",
"translation_date": "2025-08-30T01:16:45+00:00",
"source_file": "lessons/5-NLP/13-TextRep/TextRepresentationTF.ipynb",
"language_code": "bg"
}
},
"nbformat": 4,
"nbformat_minor": 4
}

View File

@ -0,0 +1,726 @@
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Вграждания\n",
"\n",
"В предишния пример работихме с високодименсионални вектори на чанта от думи с дължина `vocab_size`, като изрично преобразувахме от нискодименсионални вектори на позиционно представяне в разредено едноразрядно представяне. Това едноразрядно представяне не е ефективно по отношение на паметта, освен това всяка дума се третира независимо от останалите, т.е. едноразрядно кодираните вектори не изразяват никаква семантична прилика между думите.\n",
"\n",
"В този модул ще продължим да изследваме набора от данни **News AG**. За начало, нека заредим данните и вземем някои дефиниции от предишния тефтер.\n"
]
},
{
"cell_type": "code",
"execution_count": 1,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Loading dataset...\n"
]
},
{
"name": "stderr",
"output_type": "stream",
"text": [
"d:\\WORK\\ai-for-beginners\\5-NLP\\14-Embeddings\\data\\train.csv: 29.5MB [00:01, 18.8MB/s] \n",
"d:\\WORK\\ai-for-beginners\\5-NLP\\14-Embeddings\\data\\test.csv: 1.86MB [00:00, 11.2MB/s] \n"
]
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"Building vocab...\n",
"Vocab size = 95812\n"
]
}
],
"source": [
"import torch\n",
"import torchtext\n",
"import numpy as np\n",
"from torchnlp import *\n",
"train_dataset, test_dataset, classes, vocab = load_dataset()\n",
"vocab_size = len(vocab)\n",
"print(\"Vocab size = \",vocab_size)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Какво е вграждане?\n",
"\n",
"Идеята на **вграждането** е да представим думите чрез плътни вектори с по-ниска размерност, които по някакъв начин отразяват семантичното значение на думата. По-късно ще обсъдим как да създаваме смислени вграждания на думи, но засега нека просто мислим за вграждането като начин за намаляване на размерността на векторите на думите.\n",
"\n",
"Така че, слой за вграждане ще приема дума като вход и ще произвежда изходен вектор със зададен `embedding_size`. В известен смисъл, той е много подобен на слоя `Linear`, но вместо да приема вектор с едно горещо кодиране, ще може да приема номер на дума като вход.\n",
"\n",
"Използвайки слой за вграждане като първи слой в нашата мрежа, можем да преминем от модел на чанта с думи към модел на **чанта с вграждания**, където първо преобразуваме всяка дума в текста в съответното вграждане, а след това изчисляваме някаква агрегатна функция върху всички тези вграждания, като например `sum`, `average` или `max`.\n",
"\n",
"![Изображение, показващо класификатор с вграждане за пет последователни думи.](../../../../../translated_images/embedding-classifier-example.b77f021a7ee67eeec8e68bfe11636c5b97d6eaa067515a129bfb1d0034b1ac5b.bg.png)\n",
"\n",
"Нашата невронна мрежа за класификация ще започне със слой за вграждане, след това слой за агрегиране и линеен класификатор върху него:\n"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {},
"outputs": [],
"source": [
"class EmbedClassifier(torch.nn.Module):\n",
" def __init__(self, vocab_size, embed_dim, num_class):\n",
" super().__init__()\n",
" self.embedding = torch.nn.Embedding(vocab_size, embed_dim)\n",
" self.fc = torch.nn.Linear(embed_dim, num_class)\n",
"\n",
" def forward(self, x):\n",
" x = self.embedding(x)\n",
" x = torch.mean(x,dim=1)\n",
" return self.fc(x)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Работа с променлив размер на последователността\n",
"\n",
"В резултат на тази архитектура, минипартидите за нашата мрежа трябва да бъдат създадени по определен начин. В предишния модул, когато използвахме bag-of-words, всички BoW тензори в една минипартида имаха еднакъв размер `vocab_size`, независимо от действителната дължина на текстовата последователност. След като преминем към word embeddings, ще се окажем с променлив брой думи във всяка текстова извадка, и при комбинирането на тези извадки в минипартиди ще трябва да приложим известно запълване.\n",
"\n",
"Това може да се направи, като използваме същата техника за предоставяне на функция `collate_fn` към източника на данни:\n"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {},
"outputs": [],
"source": [
"def padify(b):\n",
" # b is the list of tuples of length batch_size\n",
" # - first element of a tuple = label, \n",
" # - second = feature (text sequence)\n",
" # build vectorized sequence\n",
" v = [encode(x[1]) for x in b]\n",
" # first, compute max length of a sequence in this minibatch\n",
" l = max(map(len,v))\n",
" return ( # tuple of two tensors - labels and features\n",
" torch.LongTensor([t[0]-1 for t in b]),\n",
" torch.stack([torch.nn.functional.pad(torch.tensor(t),(0,l-len(t)),mode='constant',value=0) for t in v])\n",
" )\n",
"\n",
"train_loader = torch.utils.data.DataLoader(train_dataset, batch_size=16, collate_fn=padify, shuffle=True)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Обучение на класификатор с вградени представяния\n",
"\n",
"Сега, след като сме дефинирали подходящия dataloader, можем да обучим модела, използвайки функцията за обучение, която дефинирахме в предишния модул:\n"
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"3200: acc=0.6415625\n",
"6400: acc=0.6865625\n",
"9600: acc=0.7103125\n",
"12800: acc=0.726953125\n",
"16000: acc=0.739375\n",
"19200: acc=0.75046875\n",
"22400: acc=0.7572321428571429\n"
]
},
{
"data": {
"text/plain": [
"(0.889799795315499, 0.7623160588611644)"
]
},
"execution_count": 4,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"net = EmbedClassifier(vocab_size,32,len(classes)).to(device)\n",
"train_epoch(net,train_loader, lr=1, epoch_size=25000)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"> **Забележка**: Тук тренираме само за 25k записа (по-малко от една пълна епоха) заради времето, но можете да продължите обучението, да напишете функция за обучение за няколко епохи и да експериментирате с параметъра на скоростта на обучение, за да постигнете по-висока точност. Трябва да можете да достигнете точност от около 90%.\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Слой EmbeddingBag и представяне на променливи по дължина последователности\n",
"\n",
"В предишната архитектура трябваше да запълним всички последователности до еднаква дължина, за да ги включим в минипартида. Това не е най-ефективният начин за представяне на последователности с променлива дължина - друг подход би бил използването на **вектор на отместванията**, който съдържа отместванията на всички последователности, съхранени в един голям вектор.\n",
"\n",
"![Изображение, показващо представяне на последователност с отмествания](../../../../../translated_images/offset-sequence-representation.eb73fcefb29b46eecfbe74466077cfeb7c0f93a4f254850538a2efbc63517479.bg.png)\n",
"\n",
"> **Note**: На изображението по-горе е показана последователност от символи, но в нашия пример работим с последователности от думи. Въпреки това, основният принцип на представяне на последователности с вектор на отмествания остава същият.\n",
"\n",
"За да работим с представяне чрез отмествания, използваме слоя [`EmbeddingBag`](https://pytorch.org/docs/stable/generated/torch.nn.EmbeddingBag.html). Той е подобен на `Embedding`, но приема вектор със съдържание и вектор с отмествания като вход, и също така включва слой за осредняване, който може да бъде `mean`, `sum` или `max`.\n",
"\n",
"Ето модифицирана мрежа, която използва `EmbeddingBag`:\n"
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {},
"outputs": [],
"source": [
"class EmbedClassifier(torch.nn.Module):\n",
" def __init__(self, vocab_size, embed_dim, num_class):\n",
" super().__init__()\n",
" self.embedding = torch.nn.EmbeddingBag(vocab_size, embed_dim)\n",
" self.fc = torch.nn.Linear(embed_dim, num_class)\n",
"\n",
" def forward(self, text, off):\n",
" x = self.embedding(text, off)\n",
" return self.fc(x)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"За да подготвим набора от данни за обучение, трябва да предоставим функция за преобразуване, която ще подготви вектора на отместване:\n"
]
},
{
"cell_type": "code",
"execution_count": 6,
"metadata": {},
"outputs": [],
"source": [
"def offsetify(b):\n",
" # first, compute data tensor from all sequences\n",
" x = [torch.tensor(encode(t[1])) for t in b]\n",
" # now, compute the offsets by accumulating the tensor of sequence lengths\n",
" o = [0] + [len(t) for t in x]\n",
" o = torch.tensor(o[:-1]).cumsum(dim=0)\n",
" return ( \n",
" torch.LongTensor([t[0]-1 for t in b]), # labels\n",
" torch.cat(x), # text \n",
" o\n",
" )\n",
"\n",
"train_loader = torch.utils.data.DataLoader(train_dataset, batch_size=16, collate_fn=offsetify, shuffle=True)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Имайте предвид, че за разлика от всички предишни примери, нашата мрежа сега приема два параметъра: вектор на данни и вектор на отместване, които са с различни размери. По същия начин, нашият модул за зареждане на данни също ни предоставя 3 стойности вместо 2: както текстовите, така и векторите на отместване се предоставят като характеристики. Следователно, трябва леко да коригираме нашата функция за обучение, за да се погрижим за това:\n"
]
},
{
"cell_type": "code",
"execution_count": 7,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"3200: acc=0.6153125\n",
"6400: acc=0.6615625\n",
"9600: acc=0.6932291666666667\n",
"12800: acc=0.715078125\n",
"16000: acc=0.7270625\n",
"19200: acc=0.7382291666666667\n",
"22400: acc=0.7486160714285715\n"
]
},
{
"data": {
"text/plain": [
"(22.771553103007037, 0.7551983365323096)"
]
},
"execution_count": 7,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"net = EmbedClassifier(vocab_size,32,len(classes)).to(device)\n",
"\n",
"def train_epoch_emb(net,dataloader,lr=0.01,optimizer=None,loss_fn = torch.nn.CrossEntropyLoss(),epoch_size=None, report_freq=200):\n",
" optimizer = optimizer or torch.optim.Adam(net.parameters(),lr=lr)\n",
" loss_fn = loss_fn.to(device)\n",
" net.train()\n",
" total_loss,acc,count,i = 0,0,0,0\n",
" for labels,text,off in dataloader:\n",
" optimizer.zero_grad()\n",
" labels,text,off = labels.to(device), text.to(device), off.to(device)\n",
" out = net(text, off)\n",
" loss = loss_fn(out,labels) #cross_entropy(out,labels)\n",
" loss.backward()\n",
" optimizer.step()\n",
" total_loss+=loss\n",
" _,predicted = torch.max(out,1)\n",
" acc+=(predicted==labels).sum()\n",
" count+=len(labels)\n",
" i+=1\n",
" if i%report_freq==0:\n",
" print(f\"{count}: acc={acc.item()/count}\")\n",
" if epoch_size and count>epoch_size:\n",
" break\n",
" return total_loss.item()/count, acc.item()/count\n",
"\n",
"\n",
"train_epoch_emb(net,train_loader, lr=4, epoch_size=25000)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Семантични вграждания: Word2Vec\n",
"\n",
"В предишния пример слоят за вграждане на модела се научи да преобразува думите във векторно представяне, но това представяне нямаше особено семантично значение. Би било добре да се научи такова векторно представяне, при което подобни думи или синоними да съответстват на вектори, които са близо един до друг според някаква векторна дистанция (например евклидово разстояние).\n",
"\n",
"За да постигнем това, трябва предварително да обучим модела за вграждане върху голяма колекция от текст по специфичен начин. Един от първите методи за обучение на семантични вграждания се нарича [Word2Vec](https://en.wikipedia.org/wiki/Word2vec). Той се базира на две основни архитектури, които се използват за създаване на разпределено представяне на думите:\n",
"\n",
" - **Непрекъсната торба с думи** (CBoW) — при тази архитектура обучаваме модела да предсказва дума въз основа на заобикалящия контекст. Даден е n-грам $(W_{-2},W_{-1},W_0,W_1,W_2)$, целта на модела е да предскаже $W_0$ от $(W_{-2},W_{-1},W_1,W_2)$.\n",
" - **Непрекъснат скип-грам** е противоположен на CBoW. Моделът използва заобикалящия прозорец от контекстни думи, за да предскаже текущата дума.\n",
"\n",
"CBoW е по-бърз, докато скип-грам е по-бавен, но се справя по-добре с представянето на редки думи.\n",
"\n",
"![Изображение, показващо алгоритмите CBoW и Skip-Gram за преобразуване на думи във вектори.](../../../../../translated_images/example-algorithms-for-converting-words-to-vectors.fbe9207a726922f6f0f5de66427e8a6eda63809356114e28fb1fa5f4a83ebda7.bg.png)\n",
"\n",
"За да експериментираме с вграждания word2vec, предварително обучени върху набора от данни Google News, можем да използваме библиотеката **gensim**. По-долу намираме думите, които са най-близки до 'neural'.\n",
"\n",
"> **Note:** Когато за първи път създавате векторни представяния на думи, изтеглянето им може да отнеме известно време!\n"
]
},
{
"cell_type": "code",
"execution_count": 8,
"metadata": {},
"outputs": [],
"source": [
"import gensim.downloader as api\n",
"w2v = api.load('word2vec-google-news-300')"
]
},
{
"cell_type": "code",
"execution_count": 9,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"neuronal -> 0.7804799675941467\n",
"neurons -> 0.7326500415802002\n",
"neural_circuits -> 0.7252851724624634\n",
"neuron -> 0.7174385190010071\n",
"cortical -> 0.6941086649894714\n",
"brain_circuitry -> 0.6923246383666992\n",
"synaptic -> 0.6699118614196777\n",
"neural_circuitry -> 0.6638563275337219\n",
"neurochemical -> 0.6555314064025879\n",
"neuronal_activity -> 0.6531826257705688\n"
]
}
],
"source": [
"for w,p in w2v.most_similar('neural'):\n",
" print(f\"{w} -> {p}\")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Можем също така да изчислим векторни вграждания от думата, които да се използват при обучението на класификационен модел (показваме само първите 20 компонента на вектора за яснота):\n"
]
},
{
"cell_type": "code",
"execution_count": 10,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"array([ 0.01226807, 0.06225586, 0.10693359, 0.05810547, 0.23828125,\n",
" 0.03686523, 0.05151367, -0.20703125, 0.01989746, 0.10058594,\n",
" -0.03759766, -0.1015625 , -0.15820312, -0.08105469, -0.0390625 ,\n",
" -0.05053711, 0.16015625, 0.2578125 , 0.10058594, -0.25976562],\n",
" dtype=float32)"
]
},
"execution_count": 10,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"w2v.word_vec('play')[:20]"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Страхотното при семантичните вграждания е, че можете да манипулирате векторното кодиране, за да промените семантиката. Например, можем да поискаме да намерим дума, чиято векторна репрезентация да бъде възможно най-близка до думите *крал* и *жена*, и възможно най-далеч от думата *мъж*:\n"
]
},
{
"cell_type": "code",
"execution_count": 10,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"('queen', 0.7118192911148071)"
]
},
"execution_count": 10,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"w2v.most_similar(positive=['king','woman'],negative=['man'])[0]"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"И CBoW, и Skip-Grams са „предсказателни“ вграждания, тъй като вземат предвид само локалните контексти. Word2Vec не използва глобалния контекст.\n",
"\n",
"**FastText** надгражда Word2Vec, като научава векторни представяния за всяка дума и за символните n-грамове, които се намират в думата. Стойностите на представянията се осредняват в един вектор на всяка стъпка от обучението. Въпреки че това добавя значително допълнително изчисление към предварителното обучение, то позволява на вгражданията на думи да кодират информация за поддуми.\n",
"\n",
"Друг метод, **GloVe**, използва идеята за матрица на съвместна поява и прилага невронни методи за разлагане на матрицата на съвместна поява в по-изразителни и нелинейни векторни представяния на думи.\n",
"\n",
"Можете да експериментирате с примера, като промените вгражданията на FastText и GloVe, тъй като gensim поддържа няколко различни модела за вграждане на думи.\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Използване на предварително обучени вграждания в PyTorch\n",
"\n",
"Можем да променим горния пример, за да предварително запълним матрицата в слоя за вграждане със семантични вграждания, като Word2Vec. Трябва да имаме предвид, че речниците на предварително обучените вграждания и нашия текстов корпус вероятно няма да съвпадат, затова ще инициализираме теглата за липсващите думи със случайни стойности:\n"
]
},
{
"cell_type": "code",
"execution_count": 11,
"metadata": {
"tags": []
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Embedding size: 300\n",
"Populating matrix, this will take some time...Done, found 41080 words, 54732 words missing\n"
]
}
],
"source": [
"embed_size = len(w2v.get_vector('hello'))\n",
"print(f'Embedding size: {embed_size}')\n",
"\n",
"net = EmbedClassifier(vocab_size,embed_size,len(classes))\n",
"\n",
"print('Populating matrix, this will take some time...',end='')\n",
"found, not_found = 0,0\n",
"for i,w in enumerate(vocab.get_itos()):\n",
" try:\n",
" net.embedding.weight[i].data = torch.tensor(w2v.get_vector(w))\n",
" found+=1\n",
" except:\n",
" net.embedding.weight[i].data = torch.normal(0.0,1.0,(embed_size,))\n",
" not_found+=1\n",
"\n",
"print(f\"Done, found {found} words, {not_found} words missing\")\n",
"net = net.to(device)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Сега нека обучим нашия модел. Имайте предвид, че времето за обучение на модела е значително по-дълго в сравнение с предишния пример, поради по-големия размер на слоя за вграждане и съответно много по-големия брой параметри. Също така, заради това може да се наложи да обучим модела си върху повече примери, ако искаме да избегнем прекомерното напасване.\n"
]
},
{
"cell_type": "code",
"execution_count": 12,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"3200: acc=0.6359375\n",
"6400: acc=0.68109375\n",
"9600: acc=0.7067708333333333\n",
"12800: acc=0.723671875\n",
"16000: acc=0.73625\n",
"19200: acc=0.7463541666666667\n",
"22400: acc=0.7560714285714286\n"
]
},
{
"data": {
"text/plain": [
"(214.1013875559821, 0.7626759436980166)"
]
},
"execution_count": 12,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"train_epoch_emb(net,train_loader, lr=4, epoch_size=25000)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"В нашия случай не наблюдаваме значително увеличение на точността, което вероятно се дължи на доста различни речници. \n",
"За да преодолеем проблема с различните речници, можем да използваме едно от следните решения: \n",
"* Преобучаване на word2vec модела върху нашия речник \n",
"* Зареждане на нашия набор от данни с речника от предварително обучен word2vec модел. Речникът, използван за зареждане на набора от данни, може да бъде зададен по време на зареждането. \n",
"\n",
"Вторият подход изглежда по-лесен, особено защото PyTorch `torchtext` рамката съдържа вградена поддръжка за вграждания. Можем, например, да създадем речник, базиран на GloVe, по следния начин: \n"
]
},
{
"cell_type": "code",
"execution_count": 14,
"metadata": {},
"outputs": [
{
"name": "stderr",
"output_type": "stream",
"text": [
"100%|█████████▉| 399999/400000 [00:15<00:00, 25411.14it/s]\n"
]
}
],
"source": [
"vocab = torchtext.vocab.GloVe(name='6B', dim=50)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Заредената речникова структура има следните основни операции: \n",
"* Речникът `vocab.stoi` ни позволява да преобразуваме дума в нейния индекс в речника \n",
"* `vocab.itos` прави обратното - преобразува число в дума \n",
"* `vocab.vectors` е масивът от векторите на вграждане, така че за да получим вграждането на дума `s`, трябва да използваме `vocab.vectors[vocab.stoi[s]]` \n",
"\n",
"Ето пример за манипулиране на вграждания, за да демонстрираме уравнението **kind-man+woman = queen** (трябваше да коригирам коефициента малко, за да проработи):\n"
]
},
{
"cell_type": "code",
"execution_count": 15,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"'queen'"
]
},
"execution_count": 15,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"# get the vector corresponding to kind-man+woman\n",
"qvec = vocab.vectors[vocab.stoi['king']]-vocab.vectors[vocab.stoi['man']]+1.3*vocab.vectors[vocab.stoi['woman']]\n",
"# find the index of the closest embedding vector \n",
"d = torch.sum((vocab.vectors-qvec)**2,dim=1)\n",
"min_idx = torch.argmin(d)\n",
"# find the corresponding word\n",
"vocab.itos[min_idx]"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"За да обучим класификатора, използвайки тези вграждания, първо трябва да кодираме нашия набор от данни, използвайки речника на GloVe:\n"
]
},
{
"cell_type": "code",
"execution_count": 16,
"metadata": {},
"outputs": [],
"source": [
"def offsetify(b):\n",
" # first, compute data tensor from all sequences\n",
" x = [torch.tensor(encode(t[1],voc=vocab)) for t in b] # pass the instance of vocab to encode function!\n",
" # now, compute the offsets by accumulating the tensor of sequence lengths\n",
" o = [0] + [len(t) for t in x]\n",
" o = torch.tensor(o[:-1]).cumsum(dim=0)\n",
" return ( \n",
" torch.LongTensor([t[0]-1 for t in b]), # labels\n",
" torch.cat(x), # text \n",
" o\n",
" )"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Както видяхме по-горе, всички векторни вграждания се съхраняват в матрицата `vocab.vectors`. Това прави изключително лесно зареждането на тези тегла в теглата на слоя за вграждане чрез просто копиране:\n"
]
},
{
"cell_type": "code",
"execution_count": 17,
"metadata": {},
"outputs": [],
"source": [
"net = EmbedClassifier(len(vocab),len(vocab.vectors[0]),len(classes))\n",
"net.embedding.weight.data = vocab.vectors\n",
"net = net.to(device)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Сега нека обучим нашия модел и да видим дали ще получим по-добри резултати:\n"
]
},
{
"cell_type": "code",
"execution_count": 18,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"3200: acc=0.6271875\n",
"6400: acc=0.68078125\n",
"9600: acc=0.7030208333333333\n",
"12800: acc=0.71984375\n",
"16000: acc=0.7346875\n",
"19200: acc=0.7455729166666667\n",
"22400: acc=0.7529464285714286\n"
]
},
{
"data": {
"text/plain": [
"(35.53972978646833, 0.7575175943698017)"
]
},
"execution_count": 18,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"train_loader = torch.utils.data.DataLoader(train_dataset, batch_size=16, collate_fn=offsetify, shuffle=True)\n",
"train_epoch_emb(net,train_loader, lr=4, epoch_size=25000)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Една от причините, поради които не наблюдаваме значително увеличение на точността, е фактът, че някои думи от нашия набор от данни липсват в предварително обучената GloVe лексика и следователно те са на практика игнорирани. За да преодолеем този факт, можем да обучим собствени вграждания върху нашия набор от данни.\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Контекстуални вграждания\n",
"\n",
"Едно от основните ограничения на традиционните предварително обучени вграждания като Word2Vec е проблемът с разграничаването на значенията на думите. Докато предварително обучените вграждания могат да уловят част от значението на думите в контекст, всяко възможно значение на дадена дума се кодира в едно и също вграждане. Това може да създаде проблеми в последващи модели, тъй като много думи, като например думата \"play\", имат различни значения в зависимост от контекста, в който се използват.\n",
"\n",
"Например, думата \"play\" в тези две различни изречения има съвсем различно значение:\n",
"- Отидох на **пиеса** в театъра.\n",
"- Джон иска да **играе** с приятелите си.\n",
"\n",
"Предварително обучените вграждания по-горе представят и двете значения на думата \"play\" в едно и също вграждане. За да преодолеем това ограничение, трябва да изградим вграждания, базирани на **езиков модел**, който е обучен върху голям корпус от текст и *знае* как думите могат да се комбинират в различни контексти. Обсъждането на контекстуални вграждания е извън обхвата на този урок, но ще се върнем към тях, когато говорим за езикови модели в следващия модул.\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"\n---\n\n**Отказ от отговорност**: \nТози документ е преведен с помощта на AI услуга за превод [Co-op Translator](https://github.com/Azure/co-op-translator). Въпреки че се стремим към точност, моля, имайте предвид, че автоматизираните преводи може да съдържат грешки или неточности. Оригиналният документ на неговия роден език трябва да се счита за авторитетен източник. За критична информация се препоръчва професионален човешки превод. Ние не носим отговорност за недоразумения или погрешни интерпретации, произтичащи от използването на този превод.\n"
]
}
],
"metadata": {
"interpreter": {
"hash": "0cb620c6d4b9f7a635928804c26cf22403d89d98d79684e4529119355ee6d5a5"
},
"kernelspec": {
"display_name": "py37_pytorch",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.8.12"
},
"coopTranslator": {
"original_hash": "f50b026abce5cf36783a560ea72cb9b1",
"translation_date": "2025-08-30T01:09:08+00:00",
"source_file": "lessons/5-NLP/14-Embeddings/EmbeddingsPyTorch.ipynb",
"language_code": "bg"
}
},
"nbformat": 4,
"nbformat_minor": 2
}

View File

@ -0,0 +1,695 @@
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Вграждания\n",
"\n",
"В предишния пример работихме с високодименсионални вектори на чанта с думи с дължина `vocab_size`, и изрично преобразувахме нискодименсионалните вектори на позиционно представяне в редки едноточкови представяния. Това едноточково представяне не е ефективно по отношение на паметта. Освен това, всяка дума се третира независимо от другите, така че едноточково кодираните вектори не изразяват семантични прилики между думите.\n",
"\n",
"В този модул ще продължим да изследваме набора от данни **News AG**. За начало, нека заредим данните и вземем някои дефиниции от предишния модул.\n"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {},
"outputs": [],
"source": [
"import tensorflow as tf\n",
"from tensorflow import keras\n",
"import tensorflow_datasets as tfds\n",
"import numpy as np\n",
"\n",
"ds_train, ds_test = tfds.load('ag_news_subset').values()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Какво е embedding?\n",
"\n",
"Идеята на **embedding** е да представим думите чрез нискоразмерни плътни вектори, които отразяват семантичното значение на думата. По-късно ще обсъдим как да създадем смислени word embeddings, но засега нека просто мислим за embedding като начин за намаляване на размерността на вектор на дума.\n",
"\n",
"Така че, embedding слой приема дума като вход и произвежда изходен вектор със зададен размер `embedding_size`. В известен смисъл, той е много подобен на слой `Dense`, но вместо да приема вектор с one-hot кодиране като вход, той може да приема номер на дума.\n",
"\n",
"Като използваме embedding слой като първи слой в нашата мрежа, можем да преминем от модел bag-of-words към модел **embedding bag**, където първо преобразуваме всяка дума в текста в съответния embedding, а след това изчисляваме някаква агрегираща функция върху всички тези embeddings, като например `sum`, `average` или `max`.\n",
"\n",
"![Изображение, показващо embedding класификатор за пет последователни думи.](../../../../../translated_images/embedding-classifier-example.b77f021a7ee67eeec8e68bfe11636c5b97d6eaa067515a129bfb1d0034b1ac5b.bg.png)\n",
"\n",
"Нашата невронна мрежа за класификация се състои от следните слоеве:\n",
"\n",
"* Слой `TextVectorization`, който приема низ като вход и произвежда тензор от номера на токени. Ще зададем разумен размер на речника `vocab_size` и ще игнорираме по-рядко използваните думи. Формата на входа ще бъде 1, а формата на изхода ще бъде $n$, тъй като ще получим $n$ токени като резултат, всеки от тях съдържащ числа от 0 до `vocab_size`.\n",
"* Слой `Embedding`, който приема $n$ числа и намалява всяко число до плътен вектор с дадена дължина (100 в нашия пример). Така входният тензор с форма $n$ ще бъде трансформиран в тензор с форма $n\\times 100$.\n",
"* Агрегиращ слой, който взема средната стойност на този тензор по първата ос, т.е. ще изчисли средната стойност на всички $n$ входни тензори, съответстващи на различни думи. За да реализираме този слой, ще използваме слой `Lambda` и ще му предадем функцията за изчисляване на средната стойност. Изходът ще има форма 100 и ще бъде численото представяне на цялата входна последователност.\n",
"* Финален `Dense` линеен класификатор.\n"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Model: \"sequential\"\n",
"_________________________________________________________________\n",
" Layer (type) Output Shape Param # \n",
"=================================================================\n",
" text_vectorization (TextVec (None, None) 0 \n",
" torization) \n",
" \n",
" embedding (Embedding) (None, None, 100) 3000000 \n",
" \n",
" lambda (Lambda) (None, 100) 0 \n",
" \n",
" dense (Dense) (None, 4) 404 \n",
" \n",
"=================================================================\n",
"Total params: 3,000,404\n",
"Trainable params: 3,000,404\n",
"Non-trainable params: 0\n",
"_________________________________________________________________\n"
]
}
],
"source": [
"vocab_size = 30000\n",
"batch_size = 128\n",
"\n",
"vectorizer = keras.layers.experimental.preprocessing.TextVectorization(max_tokens=vocab_size,input_shape=(1,))\n",
"\n",
"model = keras.models.Sequential([\n",
" vectorizer, \n",
" keras.layers.Embedding(vocab_size,100),\n",
" keras.layers.Lambda(lambda x: tf.reduce_mean(x,axis=1)),\n",
" keras.layers.Dense(4, activation='softmax')\n",
"])\n",
"model.summary()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"В `summary` таблицата, в колоната **output shape**, първото измерение на тензора `None` съответства на размера на минипартидата, а второто - на дължината на последователността от токени. Всички последователности от токени в минипартидата имат различни дължини. Ще обсъдим как да се справим с това в следващия раздел.\n",
"\n",
"Сега нека обучим мрежата:\n"
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Training vectorizer\n",
"938/938 [==============================] - 20s 20ms/step - loss: 0.7891 - acc: 0.8155 - val_loss: 0.4470 - val_acc: 0.8642\n"
]
},
{
"data": {
"text/plain": [
"<keras.callbacks.History at 0x22255515100>"
]
},
"execution_count": 4,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"def extract_text(x):\n",
" return x['title']+' '+x['description']\n",
"\n",
"def tupelize(x):\n",
" return (extract_text(x),x['label'])\n",
"\n",
"print(\"Training vectorizer\")\n",
"vectorizer.adapt(ds_train.take(500).map(extract_text))\n",
"\n",
"model.compile(loss='sparse_categorical_crossentropy',metrics=['acc'])\n",
"model.fit(ds_train.map(tupelize).batch(batch_size),validation_data=ds_test.map(tupelize).batch(batch_size))"
]
},
{
"cell_type": "markdown",
"metadata": {
"nteract": {
"transient": {
"deleting": false
}
}
},
"source": [
"> **Забележка**: изграждаме векторизатор, базиран на подмножество от данните. Това се прави с цел ускоряване на процеса и може да доведе до ситуация, в която не всички токени от нашия текст присъстват в речника. В този случай тези токени ще бъдат игнорирани, което може да доведе до леко по-ниска точност. Въпреки това, в реалния живот подмножество от текста често дава добра оценка на речника.\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Работа с променливи размери на последователности\n",
"\n",
"Нека разберем как се извършва обучението в минипартиди. В горния пример входният тензор има размерност 1 и използваме минипартиди с дължина 128, така че реалният размер на тензора е $128 \\times 1$. Въпреки това, броят на токените във всяко изречение е различен. Ако приложим слоя `TextVectorization` към един вход, броят на върнатите токени ще бъде различен в зависимост от начина, по който текстът е токенизиран:\n"
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"tf.Tensor([ 1 45], shape=(2,), dtype=int64)\n",
"tf.Tensor([ 112 1271 1 3 1747 158], shape=(6,), dtype=int64)\n"
]
}
],
"source": [
"print(vectorizer('Hello, world!'))\n",
"print(vectorizer('I am glad to meet you!'))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Въпреки това, когато приложим векторизатора към няколко последователности, той трябва да произведе тензор с правоъгълна форма, затова запълва неизползваните елементи с токена PAD (който в нашия случай е нула):\n"
]
},
{
"cell_type": "code",
"execution_count": 6,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"<tf.Tensor: shape=(2, 6), dtype=int64, numpy=\n",
"array([[ 1, 45, 0, 0, 0, 0],\n",
" [ 112, 1271, 1, 3, 1747, 158]], dtype=int64)>"
]
},
"execution_count": 6,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"vectorizer(['Hello, world!','I am glad to meet you!'])"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Тук можем да видим вгражданията:\n"
]
},
{
"cell_type": "code",
"execution_count": 7,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"array([[[ 1.53059261e-02, 6.80514947e-02, 3.14026810e-02, ...,\n",
" -8.92002955e-02, 1.52911525e-04, -5.65562584e-02],\n",
" [ 2.57456154e-01, 2.79364467e-01, -2.03605562e-01, ...,\n",
" -2.07474351e-01, 8.31158683e-02, -2.03911960e-01],\n",
" [ 3.98201384e-02, -8.03454965e-03, 2.39790026e-02, ...,\n",
" -7.18549127e-04, 2.66963355e-02, -4.30646613e-02],\n",
" [ 3.98201384e-02, -8.03454965e-03, 2.39790026e-02, ...,\n",
" -7.18549127e-04, 2.66963355e-02, -4.30646613e-02],\n",
" [ 3.98201384e-02, -8.03454965e-03, 2.39790026e-02, ...,\n",
" -7.18549127e-04, 2.66963355e-02, -4.30646613e-02],\n",
" [ 3.98201384e-02, -8.03454965e-03, 2.39790026e-02, ...,\n",
" -7.18549127e-04, 2.66963355e-02, -4.30646613e-02]],\n",
"\n",
" [[ 1.89674050e-01, 2.61548996e-01, -3.67433839e-02, ...,\n",
" -2.07366899e-01, -1.05442435e-01, -2.36952081e-01],\n",
" [ 6.16133213e-02, 1.80511594e-01, 9.77298319e-02, ...,\n",
" -5.46628237e-02, -1.07340455e-01, -1.06589928e-01],\n",
" [ 1.53059261e-02, 6.80514947e-02, 3.14026810e-02, ...,\n",
" -8.92002955e-02, 1.52911525e-04, -5.65562584e-02],\n",
" [-4.84890305e-02, -8.41715634e-02, 1.51529670e-01, ...,\n",
" 1.28192469e-01, -7.77286515e-02, 1.26041949e-01],\n",
" [-4.17212099e-02, -5.60694858e-02, 4.08860669e-02, ...,\n",
" 8.70475471e-02, 8.92383084e-02, 1.67974353e-01],\n",
" [ 2.85779923e-01, 4.57767487e-01, 4.52292450e-02, ...,\n",
" -1.97419018e-01, -2.04659685e-01, -2.79758364e-01]]],\n",
" dtype=float32)"
]
},
"execution_count": 7,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"model.layers[1](vectorizer(['Hello, world!','I am glad to meet you!'])).numpy()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"> **Забележка**: За да се минимизира количеството на допълнителните символи, в някои случаи има смисъл да се сортират всички последователности в набора от данни по реда на нарастваща дължина (или, по-точно, брой токени). Това ще гарантира, че всяка минипартида съдържа последователности с подобна дължина.\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Семантични вграждания: Word2Vec\n",
"\n",
"В предишния пример слоят за вграждане научи как да преобразува думите във векторни представяния, но тези представяния нямаха семантично значение. Би било полезно да се научи векторно представяне, при което подобни думи или синоними съответстват на вектори, които са близо един до друг според някакво векторно разстояние (например евклидово разстояние).\n",
"\n",
"За да постигнем това, трябва предварително да обучим модела за вграждане върху голяма колекция от текстове, използвайки техника като [Word2Vec](https://en.wikipedia.org/wiki/Word2vec). Тя се основава на две основни архитектури, които се използват за създаване на разпределено представяне на думите:\n",
"\n",
" - **Непрекъсната торба с думи** (CBoW), при която обучаваме модела да предсказва дума въз основа на заобикалящия контекст. Даден е n-грам $(W_{-2},W_{-1},W_0,W_1,W_2)$, като целта на модела е да предскаже $W_0$ от $(W_{-2},W_{-1},W_1,W_2)$.\n",
" - **Непрекъснат скип-грам** е противоположен на CBoW. Моделът използва заобикалящия прозорец от контекстни думи, за да предскаже текущата дума.\n",
"\n",
"CBoW е по-бърз, докато скип-грам е по-бавен, но се справя по-добре с представянето на редки думи.\n",
"\n",
"![Изображение, показващо алгоритмите CBoW и Skip-Gram за преобразуване на думи във вектори.](../../../../../translated_images/example-algorithms-for-converting-words-to-vectors.fbe9207a726922f6f0f5de66427e8a6eda63809356114e28fb1fa5f4a83ebda7.bg.png)\n",
"\n",
"За да експериментираме с вграждането Word2Vec, предварително обучено върху набора от данни Google News, можем да използваме библиотеката **gensim**. По-долу намираме думите, които са най-близки до 'neural'.\n",
"\n",
"> **Забележка:** Когато за първи път създавате векторите на думите, изтеглянето им може да отнеме известно време!\n"
]
},
{
"cell_type": "code",
"execution_count": 8,
"metadata": {},
"outputs": [],
"source": [
"import gensim.downloader as api\n",
"w2v = api.load('word2vec-google-news-300')"
]
},
{
"cell_type": "code",
"execution_count": 12,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"neuronal -> 0.7804799675941467\n",
"neurons -> 0.7326500415802002\n",
"neural_circuits -> 0.7252851724624634\n",
"neuron -> 0.7174385190010071\n",
"cortical -> 0.6941086649894714\n",
"brain_circuitry -> 0.6923246383666992\n",
"synaptic -> 0.6699118614196777\n",
"neural_circuitry -> 0.6638563275337219\n",
"neurochemical -> 0.6555314064025879\n",
"neuronal_activity -> 0.6531826257705688\n"
]
}
],
"source": [
"for w,p in w2v.most_similar('neural'):\n",
" print(f\"{w} -> {p}\")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Можем също така да извлечем векторното вграждане от думата, за да го използваме при обучението на модела за класификация. Вграждането има 300 компонента, но тук показваме само първите 20 компонента на вектора за яснота:\n"
]
},
{
"cell_type": "code",
"execution_count": 13,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"array([ 0.01226807, 0.06225586, 0.10693359, 0.05810547, 0.23828125,\n",
" 0.03686523, 0.05151367, -0.20703125, 0.01989746, 0.10058594,\n",
" -0.03759766, -0.1015625 , -0.15820312, -0.08105469, -0.0390625 ,\n",
" -0.05053711, 0.16015625, 0.2578125 , 0.10058594, -0.25976562],\n",
" dtype=float32)"
]
},
"execution_count": 13,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"w2v['play'][:20]"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Страхотното при семантичните вграждания е, че можете да манипулирате векторното кодиране въз основа на семантиката. Например, можем да поискаме да намерим дума, чиято векторна репрезентация е възможно най-близка до думите *крал* и *жена*, и възможно най-далеч от думата *мъж*:\n"
]
},
{
"cell_type": "code",
"execution_count": 14,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"('queen', 0.7118192911148071)"
]
},
"execution_count": 14,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"w2v.most_similar(positive=['king','woman'],negative=['man'])[0]"
]
},
{
"cell_type": "markdown",
"metadata": {
"tags": []
},
"source": [
"Примерът по-горе използва малко вътрешна магия на GenSym, но основната логика всъщност е доста проста. Интересното при вгражданията е, че можете да извършвате нормални операции с вектори върху векторите на вгражданията, и това би отразило операции върху **значенията** на думите. Примерът по-горе може да бъде изразен чрез операции с вектори: изчисляваме вектора, съответстващ на **KING-MAN+WOMAN** (операциите `+` и `-` се извършват върху векторните представяния на съответните думи), и след това намираме най-близката дума в речника до този вектор:\n"
]
},
{
"cell_type": "code",
"execution_count": 15,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"'queen'"
]
},
"execution_count": 15,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"# get the vector corresponding to kind-man+woman\n",
"qvec = w2v['king']-1.7*w2v['man']+1.7*w2v['woman']\n",
"# find the index of the closest embedding vector \n",
"d = np.sum((w2v.vectors-qvec)**2,axis=1)\n",
"min_idx = np.argmin(d)\n",
"# find the corresponding word\n",
"w2v.index_to_key[min_idx]"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"> **NOTE**: Трябваше да добавим малки коефициенти към векторите на *man* и *woman* - опитайте да ги премахнете, за да видите какво ще се случи.\n",
"\n",
"За да намерим най-близкия вектор, използваме механизма на TensorFlow, за да изчислим вектор от разстояния между нашия вектор и всички вектори в речника, а след това намираме индекса на минималната дума, използвайки `argmin`.\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Докато Word2Vec изглежда като чудесен начин за изразяване на семантиката на думите, той има много недостатъци, включително следните:\n",
"\n",
"* И двата модела CBoW и skip-gram са **предсказващи вграждания**, и те вземат предвид само локалния контекст. Word2Vec не използва глобалния контекст.\n",
"* Word2Vec не отчита **морфологията** на думите, т.е. факта, че значението на думата може да зависи от различни части на думата, като корена.\n",
"\n",
"**FastText** се опитва да преодолее второто ограничение и надгражда Word2Vec, като учи векторни представяния за всяка дума и n-грамите от символи, които се намират в рамките на всяка дума. Стойностите на тези представяния след това се осредняват в един вектор на всяка стъпка от обучението. Въпреки че това добавя много допълнителни изчисления към предварителното обучение, то позволява вгражданията на думите да кодират информация за поддуми.\n",
"\n",
"Друг метод, **GloVe**, използва различен подход към вгражданията на думи, базиран на факторизацията на матрицата на контекста на думите. Първо, той изгражда голяма матрица, която брои броя на срещанията на думите в различни контексти, и след това се опитва да представи тази матрица в по-ниски измерения по начин, който минимизира загубата при реконструкция.\n",
"\n",
"Библиотеката gensim поддържа тези вграждания на думи, и можете да експериментирате с тях, като промените кода за зареждане на модела по-горе.\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Използване на предварително обучени вграждания в Keras\n",
"\n",
"Можем да модифицираме горния пример, за да запълним предварително матрицата в нашия слой за вграждане със семантични вграждания, като Word2Vec. Речниците на предварително обученото вграждане и текстовия корпус вероятно няма да съвпадат, така че трябва да изберем един от тях. Тук разглеждаме двете възможни опции: използване на речника на токенизатора и използване на речника от Word2Vec вгражданията.\n",
"\n",
"### Използване на речника на токенизатора\n",
"\n",
"Когато използваме речника на токенизатора, някои от думите в речника ще имат съответстващи Word2Vec вграждания, а други ще липсват. Като се има предвид, че размерът на нашия речник е `vocab_size`, а дължината на векторите на Word2Vec вгражданията е `embed_size`, слоят за вграждане ще бъде представен чрез тегловна матрица с форма `vocab_size`$\\times$`embed_size`. Ще запълним тази матрица, като преминем през речника:\n"
]
},
{
"cell_type": "code",
"execution_count": 9,
"metadata": {
"tags": []
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Embedding size: 300\n",
"Populating matrix, this will take some time...Done, found 4551 words, 784 words missing\n"
]
}
],
"source": [
"embed_size = len(w2v.get_vector('hello'))\n",
"print(f'Embedding size: {embed_size}')\n",
"\n",
"vocab = vectorizer.get_vocabulary()\n",
"W = np.zeros((vocab_size,embed_size))\n",
"print('Populating matrix, this will take some time...',end='')\n",
"found, not_found = 0,0\n",
"for i,w in enumerate(vocab):\n",
" try:\n",
" W[i] = w2v.get_vector(w)\n",
" found+=1\n",
" except:\n",
" # W[i] = np.random.normal(0.0,0.3,size=(embed_size,))\n",
" not_found+=1\n",
"\n",
"print(f\"Done, found {found} words, {not_found} words missing\")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"За думи, които не присъстват в Word2Vec речника, можем или да ги оставим като нули, или да генерираме случаен вектор.\n",
"\n",
"Сега можем да дефинираме слой за вграждане с предварително обучени тегла:\n"
]
},
{
"cell_type": "code",
"execution_count": 10,
"metadata": {},
"outputs": [],
"source": [
"emb = keras.layers.Embedding(vocab_size,embed_size,weights=[W],trainable=False)\n",
"model = keras.models.Sequential([\n",
" vectorizer, emb,\n",
" keras.layers.Lambda(lambda x: tf.reduce_mean(x,axis=1)),\n",
" keras.layers.Dense(4, activation='softmax')\n",
"])"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": []
},
{
"cell_type": "code",
"execution_count": 11,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"938/938 [==============================] - 10s 10ms/step - loss: 1.1075 - acc: 0.7822 - val_loss: 0.9134 - val_acc: 0.8175\n"
]
},
{
"data": {
"text/plain": [
"<keras.callbacks.History at 0x2220226ef10>"
]
},
"execution_count": 11,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"model.compile(loss='sparse_categorical_crossentropy',metrics=['acc'])\n",
"model.fit(ds_train.map(tupelize).batch(batch_size),\n",
" validation_data=ds_test.map(tupelize).batch(batch_size))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"> **Note**: Забележете, че задаваме `trainable=False`, когато създаваме `Embedding`, което означава, че няма да обучаваме отново слоя Embedding. Това може да доведе до леко намаляване на точността, но ускорява процеса на обучение.\n",
"\n",
"### Използване на речник за вграждане\n",
"\n",
"Един проблем с предишния подход е, че речниците, използвани в TextVectorization и Embedding, са различни. За да преодолеем този проблем, можем да използваме едно от следните решения:\n",
"* Преобучаване на модела Word2Vec върху нашия речник.\n",
"* Зареждане на нашия набор от данни с речника от предварително обучения модел Word2Vec. Речниците, използвани за зареждане на набора от данни, могат да бъдат зададени по време на зареждането.\n",
"\n",
"Вторият подход изглежда по-лесен, затова нека го приложим. На първо място, ще създадем слой `TextVectorization` със зададен речник, взет от вгражданията на Word2Vec:\n"
]
},
{
"cell_type": "code",
"execution_count": 12,
"metadata": {},
"outputs": [],
"source": [
"vocab = list(w2v.vocab.keys())\n",
"vectorizer = keras.layers.experimental.preprocessing.TextVectorization(input_shape=(1,))\n",
"vectorizer.set_vocabulary(vocab)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Библиотеката за вграждане на думи gensim съдържа удобна функция, `get_keras_embeddings`, която автоматично ще създаде съответния слой за вграждане на Keras за вас.\n"
]
},
{
"cell_type": "code",
"execution_count": 13,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Epoch 1/5\n",
"938/938 [==============================] - 20s 14ms/step - loss: 1.3377 - acc: 0.4978 - val_loss: 1.2995 - val_acc: 0.5647\n",
"Epoch 2/5\n",
"938/938 [==============================] - 10s 10ms/step - loss: 1.2587 - acc: 0.5722 - val_loss: 1.2339 - val_acc: 0.5842\n",
"Epoch 3/5\n",
"938/938 [==============================] - 10s 10ms/step - loss: 1.1980 - acc: 0.5884 - val_loss: 1.1826 - val_acc: 0.5954\n",
"Epoch 4/5\n",
"938/938 [==============================] - 12s 13ms/step - loss: 1.1503 - acc: 0.6002 - val_loss: 1.1417 - val_acc: 0.6018\n",
"Epoch 5/5\n",
"938/938 [==============================] - 11s 12ms/step - loss: 1.1120 - acc: 0.6097 - val_loss: 1.1083 - val_acc: 0.6104\n"
]
},
{
"data": {
"text/plain": [
"<keras.callbacks.History at 0x2220ccb81c0>"
]
},
"execution_count": 13,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"model = keras.models.Sequential([\n",
" vectorizer, \n",
" w2v.get_keras_embedding(train_embeddings=False),\n",
" keras.layers.Lambda(lambda x: tf.reduce_mean(x,axis=1)),\n",
" keras.layers.Dense(4, activation='softmax')\n",
"])\n",
"model.compile(loss='sparse_categorical_crossentropy',metrics=['acc'])\n",
"model.fit(ds_train.map(tupelize).batch(128),validation_data=ds_test.map(tupelize).batch(128),epochs=5)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Една от причините, поради които не наблюдаваме по-висока точност, е, че някои думи от нашия набор от данни липсват в предварително обучената GloVe лексика и следователно те са на практика игнорирани. За да преодолеем това, можем да обучим собствени вграждания, базирани на нашия набор от данни.\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Контекстуални вграждания\n",
"\n",
"Едно от основните ограничения на традиционните предварително обучени представяния на вграждания като Word2Vec е фактът, че, въпреки че могат да уловят някакво значение на дадена дума, те не могат да различат различните ѝ значения. Това може да създаде проблеми в последващите модели.\n",
"\n",
"Например, думата „play“ има различно значение в тези две изречения:\n",
"- Отидох на **пиеса** в театъра.\n",
"- Джон иска да **играе** с приятелите си.\n",
"\n",
"Предварително обучените вграждания, за които говорихме, представят и двете значения на думата „play“ в едно и също вграждане. За да преодолеем това ограничение, трябва да изградим вграждания, базирани на **езиков модел**, който е обучен върху голям корпус от текст и *знае* как думите могат да се комбинират в различни контексти. Обсъждането на контекстуални вграждания е извън обхвата на този урок, но ще се върнем към тях, когато говорим за езикови модели в следващия модул.\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"\n---\n\n**Отказ от отговорност**: \nТози документ е преведен с помощта на AI услуга за превод [Co-op Translator](https://github.com/Azure/co-op-translator). Въпреки че се стремим към точност, моля, имайте предвид, че автоматизираните преводи може да съдържат грешки или неточности. Оригиналният документ на неговия роден език трябва да се счита за авторитетен източник. За критична информация се препоръчва професионален човешки превод. Ние не носим отговорност за недоразумения или погрешни интерпретации, произтичащи от използването на този превод.\n"
]
}
],
"metadata": {
"interpreter": {
"hash": "0cb620c6d4b9f7a635928804c26cf22403d89d98d79684e4529119355ee6d5a5"
},
"kernel_info": {
"name": "conda-env-py37_tensorflow-py"
},
"kernelspec": {
"display_name": "py37_tensorflow",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.8.12"
},
"nteract": {
"version": "nteract-front-end@1.0.0"
},
"coopTranslator": {
"original_hash": "b859482be7f61d1eadc2c6a2720a37e4",
"translation_date": "2025-08-30T01:04:39+00:00",
"source_file": "lessons/5-NLP/14-Embeddings/EmbeddingsTF.ipynb",
"language_code": "bg"
}
},
"nbformat": 4,
"nbformat_minor": 4
}

View File

@ -0,0 +1,576 @@
{
"cells": [
{
"cell_type": "markdown",
"metadata": {
"id": "NXTSugt6ieXh"
},
"source": [
"## Обучение на CBoW модел\n",
"\n",
"Този ноутбук е част от [AI for Beginners Curriculum](http://aka.ms/ai-beginners)\n",
"\n",
"В този пример ще разгледаме обучението на езиков модел CBoW, за да създадем собствено векторно представяне на думи (Word2Vec embedding space). Ще използваме набора от данни AG News като източник на текст.\n"
]
},
{
"cell_type": "code",
"source": [
"import torch\n",
"import torchtext\n",
"import os\n",
"import collections\n",
"import builtins\n",
"import random\n",
"import numpy as np"
],
"metadata": {
"id": "q-UiiJUKaxHj"
},
"execution_count": null,
"outputs": []
},
{
"cell_type": "code",
"source": [
"device = torch.device(\"cuda\" if torch.cuda.is_available() else \"cpu\")"
],
"metadata": {
"id": "TFbR8CZaTZ1q"
},
"execution_count": null,
"outputs": []
},
{
"cell_type": "markdown",
"source": [
"Първо нека заредим нашия набор от данни и да дефинираме токенизатор и речник. Ще зададем `vocab_size` на 5000, за да ограничим изчисленията малко.\n"
],
"metadata": {
"id": "HIwC7lI5T-ov"
}
},
{
"cell_type": "code",
"source": [
"def load_dataset(ngrams = 1, min_freq = 1, vocab_size = 5000 , lines_cnt = 500):\n",
" tokenizer = torchtext.data.utils.get_tokenizer('basic_english')\n",
" print(\"Loading dataset...\")\n",
" test_dataset, train_dataset = torchtext.datasets.AG_NEWS(root='./data')\n",
" train_dataset = list(train_dataset)\n",
" test_dataset = list(test_dataset)\n",
" classes = ['World', 'Sports', 'Business', 'Sci/Tech']\n",
" print('Building vocab...')\n",
" counter = collections.Counter()\n",
" for i, (_, line) in enumerate(train_dataset):\n",
" counter.update(torchtext.data.utils.ngrams_iterator(tokenizer(line),ngrams=ngrams))\n",
" if i == lines_cnt:\n",
" break\n",
" vocab = torchtext.vocab.Vocab(collections.Counter(dict(counter.most_common(vocab_size))), min_freq=min_freq)\n",
" return train_dataset, test_dataset, classes, vocab, tokenizer"
],
"metadata": {
"id": "wdZuygtgiuLG"
},
"execution_count": null,
"outputs": []
},
{
"cell_type": "code",
"source": [
"train_dataset, test_dataset, _, vocab, tokenizer = load_dataset()"
],
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/"
},
"id": "4d1nU1gsivGu",
"outputId": "949fe272-ae0e-49f5-c373-6703458b3a74"
},
"execution_count": null,
"outputs": [
{
"output_type": "stream",
"name": "stdout",
"text": [
"Loading dataset...\n",
"Building vocab...\n"
]
}
]
},
{
"cell_type": "code",
"source": [
"def encode(x, vocabulary, tokenizer = tokenizer):\n",
" return [vocabulary[s] for s in tokenizer(x)]"
],
"metadata": {
"id": "1XDYNhG8ToFV"
},
"execution_count": null,
"outputs": []
},
{
"cell_type": "markdown",
"metadata": {
"id": "LIlQk6_PaHVY"
},
"source": [
"## Модел CBoW\n",
"\n",
"CBoW се учи да предсказва дума въз основа на $2N$ съседни думи. Например, когато $N=1$, ще получим следните двойки от изречението *I like to train networks*: (like,I), (I, like), (to, like), (like,to), (train,to), (to, train), (networks, train), (train,networks). Тук първата дума е съседната дума, използвана като вход, а втората дума е тази, която предсказваме.\n",
"\n",
"За да изградим мрежа, която предсказва следващата дума, ще трябва да подадем съседната дума като вход и да получим номера на думата като изход. Архитектурата на мрежата CBoW е следната:\n",
"\n",
"* Входната дума преминава през слой за вграждане (embedding layer). Този слой за вграждане ще бъде нашето Word2Vec вграждане, затова ще го дефинираме отделно като променлива `embedder`. В този пример ще използваме размер на вграждането = 30, въпреки че може да искате да експериментирате с по-големи размерности (реалният Word2Vec има 300).\n",
"* Векторът на вграждането след това ще бъде подаден към линеен слой, който ще предсказва изходната дума. Следователно той има `vocab_size` неврони.\n",
"\n",
"За изхода, ако използваме `CrossEntropyLoss` като функция за загуба, ще трябва да предоставим само номерата на думите като очаквани резултати, без едно-горещо кодиране (one-hot encoding).\n"
]
},
{
"cell_type": "code",
"source": [
"vocab_size = len(vocab)\n",
"\n",
"embedder = torch.nn.Embedding(num_embeddings = vocab_size, embedding_dim = 30)\n",
"model = torch.nn.Sequential(\n",
" embedder,\n",
" torch.nn.Linear(in_features = 30, out_features = vocab_size),\n",
")\n",
"\n",
"print(model)"
],
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/"
},
"id": "akKTcKQKkfl2",
"outputId": "da687e3e-a8ec-4c1a-e456-ab8cd6ac7dad"
},
"execution_count": null,
"outputs": [
{
"output_type": "stream",
"name": "stdout",
"text": [
"Sequential(\n",
" (0): Embedding(5002, 30)\n",
" (1): Linear(in_features=30, out_features=5002, bias=True)\n",
")\n"
]
}
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "Nud6jgGPaHVa"
},
"source": [
"## Подготовка на тренировъчни данни\n",
"\n",
"Сега нека програмираме основната функция, която ще изчислява CBoW двойки думи от текст. Тази функция ще ни позволи да зададем размер на прозореца и ще върне набор от двойки - входна и изходна дума. Имайте предвид, че тази функция може да се използва както за думи, така и за вектори/тензори - което ще ни позволи да кодираме текста, преди да го подадем на функцията `to_cbow`.\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/"
},
"id": "x-dsXygOieXn",
"outputId": "c2218280-e540-40ba-9546-efe48d0d714f"
},
"outputs": [
{
"output_type": "stream",
"name": "stdout",
"text": [
"[['like', 'I'], ['to', 'I'], ['I', 'like'], ['to', 'like'], ['train', 'like'], ['I', 'to'], ['like', 'to'], ['train', 'to'], ['networks', 'to'], ['like', 'train'], ['to', 'train'], ['networks', 'train'], ['to', 'networks'], ['train', 'networks']]\n",
"[[232, 172], [5, 172], [172, 232], [5, 232], [0, 232], [172, 5], [232, 5], [0, 5], [1202, 5], [232, 0], [5, 0], [1202, 0], [5, 1202], [0, 1202]]\n"
]
}
],
"source": [
"def to_cbow(sent,window_size=2):\n",
" res = []\n",
" for i,x in enumerate(sent):\n",
" for j in range(max(0,i-window_size),min(i+window_size+1,len(sent))):\n",
" if i!=j:\n",
" res.append([sent[j],x])\n",
" return res\n",
"\n",
"print(to_cbow(['I','like','to','train','networks']))\n",
"print(to_cbow(encode('I like to train networks', vocab)))"
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "XVaaDLjaaHVb"
},
"source": [
"Нека подготвим тренировъчния набор от данни. Ще преминем през всички новини, ще извикаме `to_cbow`, за да получим списъка с двойки думи, и ще добавим тези двойки към `X` и `Y`. За да спестим време, ще разгледаме само първите 10k новини - лесно можете да премахнете ограничението, ако имате повече време за изчакване и искате да получите по-добри вграждания :)\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"id": "54b-Gd9TieXo"
},
"outputs": [],
"source": [
"X = []\n",
"Y = []\n",
"for i, x in zip(range(10000), train_dataset):\n",
" for w1, w2 in to_cbow(encode(x[1], vocab), window_size = 5):\n",
" X.append(w1)\n",
" Y.append(w2)\n",
"\n",
"X = torch.tensor(X)\n",
"Y = torch.tensor(Y)"
]
},
{
"cell_type": "markdown",
"source": [
"Ще преобразуваме тези данни в един набор от данни и ще създадем dataloader:\n"
],
"metadata": {
"id": "cwWy0PzXWhN5"
}
},
{
"cell_type": "code",
"source": [
"class SimpleIterableDataset(torch.utils.data.IterableDataset):\n",
" def __init__(self, X, Y):\n",
" super(SimpleIterableDataset).__init__()\n",
" self.data = []\n",
" for i in range(len(X)):\n",
" self.data.append( (Y[i], X[i]) )\n",
" random.shuffle(self.data)\n",
"\n",
" def __iter__(self):\n",
" return iter(self.data)"
],
"metadata": {
"id": "mfoAcGPFZU8p"
},
"execution_count": null,
"outputs": []
},
{
"cell_type": "markdown",
"metadata": {
"id": "e4NQ_-5waHVc"
},
"source": [
"Ще преобразуваме тези данни в един набор от данни и ще създадем dataloader:\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"id": "AbLUcojlieXo"
},
"outputs": [],
"source": [
"ds = SimpleIterableDataset(X, Y)\n",
"dl = torch.utils.data.DataLoader(ds, batch_size = 256)"
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "pKQr7sXeaHVc"
},
"source": [
"Сега нека преминем към действителното обучение. Ще използваме оптимизатора `SGD` с доста висока скорост на обучение. Можете също да опитате да експериментирате с други оптимизатори, като например `Adam`. Ще обучаваме за 10 епохи за начало - и можете да изпълните тази клетка отново, ако искате още по-ниска загуба.\n"
]
},
{
"cell_type": "code",
"source": [
"def train_epoch(net, dataloader, lr = 0.01, optimizer = None, loss_fn = torch.nn.CrossEntropyLoss(), epochs = None, report_freq = 1):\n",
" optimizer = optimizer or torch.optim.Adam(net.parameters(), lr = lr)\n",
" loss_fn = loss_fn.to(device)\n",
" net.train()\n",
"\n",
" for i in range(epochs):\n",
" total_loss, j = 0, 0, \n",
" for labels, features in dataloader:\n",
" optimizer.zero_grad()\n",
" features, labels = features.to(device), labels.to(device)\n",
" out = net(features)\n",
" loss = loss_fn(out, labels)\n",
" loss.backward()\n",
" optimizer.step()\n",
" total_loss += loss\n",
" j += 1\n",
" if i % report_freq == 0:\n",
" print(f\"Epoch: {i+1}: loss={total_loss.item()/j}\")\n",
"\n",
" return total_loss.item()/j"
],
"metadata": {
"id": "HeeCYKr_KF1w"
},
"execution_count": null,
"outputs": []
},
{
"cell_type": "code",
"source": [
"train_epoch(net = model, dataloader = dl, optimizer = torch.optim.SGD(model.parameters(), lr = 0.1), loss_fn = torch.nn.CrossEntropyLoss(), epochs = 10)"
],
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/"
},
"id": "KVgwGtDHgDlT",
"outputId": "2447833f-f0e3-4566-c33d-addbfe2f451d"
},
"execution_count": null,
"outputs": [
{
"output_type": "stream",
"name": "stdout",
"text": [
"Epoch: 1: loss=5.664632366860172\n",
"Epoch: 2: loss=5.632101973960962\n",
"Epoch: 3: loss=5.610399051405015\n",
"Epoch: 4: loss=5.594621561080262\n",
"Epoch: 5: loss=5.582538017415446\n",
"Epoch: 6: loss=5.572900234519603\n",
"Epoch: 7: loss=5.564951676341915\n",
"Epoch: 8: loss=5.558288112064614\n",
"Epoch: 9: loss=5.552576955031129\n",
"Epoch: 10: loss=5.547634165194347\n"
]
},
{
"output_type": "execute_result",
"data": {
"text/plain": [
"5.547634165194347"
]
},
"metadata": {},
"execution_count": 16
}
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "W8u2qXZmaHVd"
},
"source": [
"## Изпробване на Word2Vec\n",
"\n",
"За да използваме Word2Vec, нека извлечем вектори, съответстващи на всички думи в нашия речник:\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"id": "r8TatcXjkU_t"
},
"outputs": [],
"source": [
"vectors = torch.stack([embedder(torch.tensor(vocab[s])) for s in vocab.itos], 0)"
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "3OcX21UOaHVd"
},
"source": [
"Нека видим, например, как думата **Париж** се кодира в вектор:\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/"
},
"id": "bz6tAeLzieXp",
"outputId": "5b20850e-4342-45e9-f840-cfac2b4d61d8"
},
"outputs": [
{
"output_type": "stream",
"name": "stdout",
"text": [
"tensor([-0.0915, 2.1224, -0.0281, -0.6819, 1.1219, 0.6458, -1.3704, -1.3314,\n",
" -1.1437, 0.4496, 0.2301, -0.3515, -0.8485, 1.0481, 0.4386, -0.8949,\n",
" 0.5644, 1.0939, -2.5096, 3.2949, -0.2601, -0.8640, 0.1421, -0.0804,\n",
" -0.5083, -1.0560, 0.9753, -0.5949, -1.6046, 0.5774],\n",
" grad_fn=<EmbeddingBackward>)\n"
]
}
],
"source": [
"paris_vec = embedder(torch.tensor(vocab['paris']))\n",
"print(paris_vec)"
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "pHTJlaeYaHVd"
},
"source": [
"Интересно е да се използва Word2Vec за търсене на синоними. Следната функция ще върне `n` най-близки думи до даден вход. За да ги намерим, изчисляваме нормата на $|w_i - v|$, където $v$ е векторът, съответстващ на нашата входна дума, а $w_i$ е кодирането на $i$-тата дума в речника. След това сортираме масива и връщаме съответните индекси, използвайки `argsort`, и вземаме първите `n` елемента от списъка, които кодират позициите на най-близките думи в речника.\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/"
},
"id": "NlZyi-_olFar",
"outputId": "b5dbb163-88c4-4d5a-eaf2-6751f700e98c"
},
"outputs": [
{
"output_type": "execute_result",
"data": {
"text/plain": [
"['microsoft', 'quoted', 'lp', 'rate', 'top']"
]
},
"metadata": {},
"execution_count": 56
}
],
"source": [
"def close_words(x, n = 5):\n",
" vec = embedder(torch.tensor(vocab[x]))\n",
" top5 = np.linalg.norm(vectors.detach().numpy() - vec.detach().numpy(), axis = 1).argsort()[:n]\n",
" return [ vocab.itos[x] for x in top5 ]\n",
"\n",
"close_words('microsoft')"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/"
},
"id": "-dQq7xeAln0U",
"outputId": "66f768c3-c248-4bfd-ce4f-c8ffc6d0dd0d"
},
"outputs": [
{
"output_type": "execute_result",
"data": {
"text/plain": [
"['basketball', 'lot', 'sinai', 'states', 'healthdaynews']"
]
},
"metadata": {},
"execution_count": 51
}
],
"source": [
"close_words('basketball')"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/"
},
"id": "fJXqK26b29sa",
"outputId": "78f0baba-ffd0-485a-dd87-0a12bedfd7fa"
},
"outputs": [
{
"output_type": "execute_result",
"data": {
"text/plain": [
"['funds', 'travel', 'sydney', 'japan', 'business']"
]
},
"metadata": {},
"execution_count": 77
}
],
"source": [
"close_words('funds')"
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "My0VeTDd3Ji8"
},
"source": [
"## Основни моменти\n",
"\n",
"Използвайки умни техники като CBoW, можем да обучим модел Word2Vec. Можете също така да опитате да обучите модел skip-gram, който е създаден да предсказва съседната дума, като се даде централната, и да видите колко добре се представя.\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"\n---\n\n**Отказ от отговорност**: \nТози документ е преведен с помощта на AI услуга за превод [Co-op Translator](https://github.com/Azure/co-op-translator). Въпреки че се стремим към точност, моля, имайте предвид, че автоматизираните преводи може да съдържат грешки или неточности. Оригиналният документ на неговия роден език трябва да се счита за авторитетен източник. За критична информация се препоръчва професионален човешки превод. Ние не носим отговорност за недоразумения или погрешни интерпретации, произтичащи от използването на този превод.\n"
]
}
],
"metadata": {
"colab": {
"collapsed_sections": [],
"name": "CBoW-PyTorch.ipynb",
"provenance": []
},
"interpreter": {
"hash": "16af2a8bbb083ea23e5e41c7f5787656b2ce26968575d8763f2c4b17f9cd711f"
},
"kernelspec": {
"display_name": "Python 3.8.12 ('py38')",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.8.12"
},
"orig_nbformat": 4,
"gpuClass": "standard",
"coopTranslator": {
"original_hash": "36df28efe3fe40b6fb0a7fa48fe3ea82",
"translation_date": "2025-08-30T00:40:35+00:00",
"source_file": "lessons/5-NLP/15-LanguageModeling/CBoW-PyTorch.ipynb",
"language_code": "bg"
}
},
"nbformat": 4,
"nbformat_minor": 0
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,479 @@
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Рекурентни невронни мрежи\n",
"\n",
"В предишния модул използвахме богати семантични представяния на текста и прост линеен класификатор върху ембедингите. Това, което прави тази архитектура, е да улавя обобщеното значение на думите в изречението, но не взема предвид **реда** на думите, защото операцията по агрегиране върху ембедингите премахва тази информация от оригиналния текст. Тъй като тези модели не могат да моделират реда на думите, те не могат да решават по-сложни или двусмислени задачи като генериране на текст или отговаряне на въпроси.\n",
"\n",
"За да уловим значението на текстовата последователност, трябва да използваме друга архитектура на невронна мрежа, наречена **рекурентна невронна мрежа** или RNN. В RNN подаваме изречението си през мрежата символ по символ, а мрежата произвежда някакво **състояние**, което след това подаваме отново на мрежата заедно със следващия символ.\n",
"\n",
"Дадена входна последователност от токени $X_0,\\dots,X_n$, RNN създава последователност от блокове на невронната мрежа и обучава тази последователност от край до край чрез обратна пропагация. Всеки блок на мрежата приема двойка $(X_i,S_i)$ като вход и произвежда $S_{i+1}$ като резултат. Финалното състояние $S_n$ или изходът $X_n$ се подава на линеен класификатор, за да се произведе резултатът. Всички блокове на мрежата споделят едни и същи тегла и се обучават от край до край с един пас на обратна пропагация.\n",
"\n",
"Тъй като векторите на състоянието $S_0,\\dots,S_n$ се предават през мрежата, тя може да научи последователните зависимости между думите. Например, когато думата *не* се появи някъде в последователността, мрежата може да научи да отрича определени елементи в състоянието, което води до отрицание.\n",
"\n",
"> Тъй като теглата на всички блокове на RNN на картинката са споделени, същата картинка може да бъде представена като един блок (вдясно) с рекурентен обратен цикъл, който предава изходното състояние на мрежата обратно към входа.\n",
"\n",
"Нека видим как рекурентните невронни мрежи могат да ни помогнат да класифицираме нашия набор от новини.\n"
]
},
{
"cell_type": "code",
"execution_count": 1,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Loading dataset...\n",
"Building vocab...\n"
]
}
],
"source": [
"import torch\n",
"import torchtext\n",
"from torchnlp import *\n",
"train_dataset, test_dataset, classes, vocab = load_dataset()\n",
"vocab_size = len(vocab)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Прост RNN класификатор\n",
"\n",
"При прост RNN всяка рекурентна единица представлява проста линейна мрежа, която приема конкатениран входен вектор и вектор на състоянието и произвежда нов вектор на състоянието. PyTorch представя тази единица чрез класа `RNNCell`, а мрежа от такива клетки - като слой `RNN`.\n",
"\n",
"За да дефинираме RNN класификатор, първо ще приложим слой за вграждане, за да намалим размерността на входния речник, а след това ще добавим слой RNN върху него:\n"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {},
"outputs": [],
"source": [
"class RNNClassifier(torch.nn.Module):\n",
" def __init__(self, vocab_size, embed_dim, hidden_dim, num_class):\n",
" super().__init__()\n",
" self.hidden_dim = hidden_dim\n",
" self.embedding = torch.nn.Embedding(vocab_size, embed_dim)\n",
" self.rnn = torch.nn.RNN(embed_dim,hidden_dim,batch_first=True)\n",
" self.fc = torch.nn.Linear(hidden_dim, num_class)\n",
"\n",
" def forward(self, x):\n",
" batch_size = x.size(0)\n",
" x = self.embedding(x)\n",
" x,h = self.rnn(x)\n",
" return self.fc(x.mean(dim=1))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"> **Note:** Тук използваме необучен слой за вграждане за простота, но за още по-добри резултати можем да използваме предварително обучен слой за вграждане с Word2Vec или GloVe вграждания, както е описано в предишния модул. За по-добро разбиране може да адаптирате този код, за да работи с предварително обучени вграждания.\n",
"\n",
"В нашия случай ще използваме зареждач на данни с допълване, така че всяка партида ще съдържа определен брой допълнени последователности с еднаква дължина. RNN слоят ще приеме последователността от тензори за вграждане и ще произведе два изхода:\n",
"* $x$ е последователност от изходи на RNN клетките на всяка стъпка\n",
"* $h$ е крайното скрито състояние за последния елемент от последователността\n",
"\n",
"След това прилагаме напълно свързан линеен класификатор, за да получим броя на класовете.\n",
"\n",
"> **Note:** RNN мрежите са доста трудни за обучение, защото когато клетките на RNN се разгънат по дължината на последователността, броят на слоевете, участващи в обратното разпространение, става доста голям. Затова е необходимо да изберем малка скорост на обучение и да обучим мрежата върху по-голям набор от данни, за да постигнем добри резултати. Това може да отнеме доста време, така че използването на GPU е за предпочитане.\n"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {
"scrolled": true
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"3200: acc=0.3090625\n",
"6400: acc=0.38921875\n",
"9600: acc=0.4590625\n",
"12800: acc=0.511953125\n",
"16000: acc=0.5506875\n",
"19200: acc=0.57921875\n",
"22400: acc=0.6070089285714285\n",
"25600: acc=0.6304296875\n",
"28800: acc=0.6484027777777778\n",
"32000: acc=0.66509375\n",
"35200: acc=0.6790056818181818\n",
"38400: acc=0.6929166666666666\n",
"41600: acc=0.7035817307692308\n",
"44800: acc=0.7137276785714286\n",
"48000: acc=0.72225\n",
"51200: acc=0.73001953125\n",
"54400: acc=0.7372794117647059\n",
"57600: acc=0.7436631944444444\n",
"60800: acc=0.7503947368421052\n",
"64000: acc=0.75634375\n",
"67200: acc=0.7615773809523809\n",
"70400: acc=0.7662642045454545\n",
"73600: acc=0.7708423913043478\n",
"76800: acc=0.7751822916666666\n",
"80000: acc=0.7790625\n",
"83200: acc=0.7825\n",
"86400: acc=0.7858564814814815\n",
"89600: acc=0.7890513392857142\n",
"92800: acc=0.7920474137931034\n",
"96000: acc=0.7952708333333334\n",
"99200: acc=0.7982258064516129\n",
"102400: acc=0.80099609375\n",
"105600: acc=0.8037594696969697\n",
"108800: acc=0.8060569852941176\n"
]
}
],
"source": [
"train_loader = torch.utils.data.DataLoader(train_dataset, batch_size=16, collate_fn=padify, shuffle=True)\n",
"net = RNNClassifier(vocab_size,64,32,len(classes)).to(device)\n",
"train_epoch(net,train_loader, lr=0.001)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Дългосрочна краткосрочна памет (LSTM)\n",
"\n",
"Един от основните проблеми на класическите RNN е така нареченият проблем с **изчезващите градиенти**. Тъй като RNN се обучават от край до край в един пас на обратна пропагация, те срещат трудности при предаването на грешката към първите слоеве на мрежата, което води до невъзможност да се научат връзките между отдалечени токени. Един от начините за избягване на този проблем е въвеждането на **явно управление на състоянието** чрез използването на така наречените **гейтове**. Две от най-известните архитектури от този тип са **Дългосрочна краткосрочна памет** (LSTM) и **Единица с управлявано предаване** (GRU).\n",
"\n",
"![Изображение, показващо пример за клетка с дългосрочна краткосрочна памет](../../../../../lessons/5-NLP/16-RNN/images/long-short-term-memory-cell.svg)\n",
"\n",
"LSTM мрежата е организирана по начин, подобен на RNN, но има две състояния, които се предават от слой на слой: действително състояние $c$ и скрит вектор $h$. Във всяка единица скритият вектор $h_i$ се конкатенира с входа $x_i$, и те контролират какво се случва със състоянието $c$ чрез **гейтове**. Всеки гейт е невронна мрежа със сигмоидна активация (изход в диапазона $[0,1]$), която може да се разглежда като битова маска, когато се умножи със състоянието. Съществуват следните гейтове (отляво надясно на изображението по-горе):\n",
"* **гейт за забравяне** взема скрития вектор и определя кои компоненти на вектора $c$ трябва да забравим и кои да пропуснем.\n",
"* **входен гейт** взема информация от входа и скрития вектор и я вмъква в състоянието.\n",
"* **изходен гейт** трансформира състоянието чрез някакъв линеен слой с $\\tanh$ активация, след което избира някои от неговите компоненти, използвайки скрития вектор $h_i$, за да произведе ново състояние $c_{i+1}$.\n",
"\n",
"Компонентите на състоянието $c$ могат да се разглеждат като някакви флагове, които могат да бъдат включени и изключени. Например, когато срещнем име *Алис* в последователността, може да предположим, че то се отнася за женски персонаж, и да вдигнем флаг в състоянието, че имаме женски съществителен в изречението. Когато по-нататък срещнем фразата *и Том*, ще вдигнем флаг, че имаме множествено число. Така, чрез манипулиране на състоянието, можем да следим граматичните свойства на частите на изречението.\n",
"\n",
"> **Note**: Отличен ресурс за разбиране на вътрешната структура на LSTM е тази страхотна статия [Understanding LSTM Networks](https://colah.github.io/posts/2015-08-Understanding-LSTMs/) от Кристофър Ола.\n",
"\n",
"Въпреки че вътрешната структура на LSTM клетката може да изглежда сложна, PyTorch скрива тази имплементация вътре в класа `LSTMCell` и предоставя обект `LSTM`, който представлява целия LSTM слой. Следователно, имплементацията на LSTM класификатор ще бъде доста подобна на простия RNN, който разгледахме по-горе:\n"
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {},
"outputs": [],
"source": [
"class LSTMClassifier(torch.nn.Module):\n",
" def __init__(self, vocab_size, embed_dim, hidden_dim, num_class):\n",
" super().__init__()\n",
" self.hidden_dim = hidden_dim\n",
" self.embedding = torch.nn.Embedding(vocab_size, embed_dim)\n",
" self.embedding.weight.data = torch.randn_like(self.embedding.weight.data)-0.5\n",
" self.rnn = torch.nn.LSTM(embed_dim,hidden_dim,batch_first=True)\n",
" self.fc = torch.nn.Linear(hidden_dim, num_class)\n",
"\n",
" def forward(self, x):\n",
" batch_size = x.size(0)\n",
" x = self.embedding(x)\n",
" x,(h,c) = self.rnn(x)\n",
" return self.fc(h[-1])"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": []
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"3200: acc=0.259375\n",
"6400: acc=0.25859375\n",
"9600: acc=0.26177083333333334\n",
"12800: acc=0.2784375\n",
"16000: acc=0.313\n",
"19200: acc=0.3528645833333333\n",
"22400: acc=0.3965625\n",
"25600: acc=0.4385546875\n",
"28800: acc=0.4752777777777778\n",
"32000: acc=0.505375\n",
"35200: acc=0.5326704545454546\n",
"38400: acc=0.5557552083333334\n",
"41600: acc=0.5760817307692307\n",
"44800: acc=0.5954910714285714\n",
"48000: acc=0.6118333333333333\n",
"51200: acc=0.62681640625\n",
"54400: acc=0.6404779411764706\n",
"57600: acc=0.6520138888888889\n",
"60800: acc=0.662828947368421\n",
"64000: acc=0.673546875\n",
"67200: acc=0.6831547619047619\n",
"70400: acc=0.6917897727272727\n",
"73600: acc=0.6997146739130434\n",
"76800: acc=0.707109375\n",
"80000: acc=0.714075\n",
"83200: acc=0.7209134615384616\n",
"86400: acc=0.727037037037037\n",
"89600: acc=0.7326674107142858\n",
"92800: acc=0.7379633620689655\n",
"96000: acc=0.7433645833333333\n",
"99200: acc=0.7479032258064516\n",
"102400: acc=0.752119140625\n",
"105600: acc=0.7562405303030303\n",
"108800: acc=0.76015625\n",
"112000: acc=0.7641339285714286\n",
"115200: acc=0.7677777777777778\n",
"118400: acc=0.7711233108108108\n"
]
},
{
"data": {
"text/plain": [
"(0.03487814127604167, 0.7728)"
]
},
"execution_count": 5,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"net = LSTMClassifier(vocab_size,64,32,len(classes)).to(device)\n",
"train_epoch(net,train_loader, lr=0.001)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Опаковани последователности\n",
"\n",
"В нашия пример трябваше да запълним всички последователности в минипартидата с нулеви вектори. Това води до известно разхищение на памет, но при RNN е още по-критично, защото се създават допълнителни клетки на RNN за запълнените входни елементи, които участват в обучението, но не носят никаква важна входна информация. Би било много по-добре RNN да се обучава само до реалния размер на последователността.\n",
"\n",
"За да се постигне това, в PyTorch е въведен специален формат за съхранение на запълнени последователности. Да предположим, че имаме входна запълнена минипартида, която изглежда така:\n",
"```\n",
"[[1,2,3,4,5],\n",
" [6,7,8,0,0],\n",
" [9,0,0,0,0]]\n",
"```\n",
"Тук 0 представлява запълнените стойности, а векторът с действителната дължина на входните последователности е `[5,3,1]`.\n",
"\n",
"За да обучим ефективно RNN със запълнени последователности, искаме да започнем обучението на първата група клетки на RNN с голяма минипартида (`[1,6,9]`), но след това да приключим обработката на третата последователност и да продължим обучението с по-малки минипартиди (`[2,7]`, `[3,8]`) и т.н. Така опакованата последователност се представя като един вектор - в нашия случай `[1,6,9,2,7,3,8,4,5]`, и вектор на дължините (`[5,3,1]`), от който лесно можем да възстановим оригиналната запълнена минипартида.\n",
"\n",
"За да създадем опакована последователност, можем да използваме функцията `torch.nn.utils.rnn.pack_padded_sequence`. Всички рекурентни слоеве, включително RNN, LSTM и GRU, поддържат опаковани последователности като вход и произвеждат опакован изход, който може да бъде декодиран с помощта на `torch.nn.utils.rnn.pad_packed_sequence`.\n",
"\n",
"За да можем да създадем опакована последователност, трябва да предадем вектора на дължините към мрежата, и затова ни е необходима различна функция за подготовка на минипартидите:\n"
]
},
{
"cell_type": "code",
"execution_count": 6,
"metadata": {},
"outputs": [],
"source": [
"def pad_length(b):\n",
" # build vectorized sequence\n",
" v = [encode(x[1]) for x in b]\n",
" # compute max length of a sequence in this minibatch and length sequence itself\n",
" len_seq = list(map(len,v))\n",
" l = max(len_seq)\n",
" return ( # tuple of three tensors - labels, padded features, length sequence\n",
" torch.LongTensor([t[0]-1 for t in b]),\n",
" torch.stack([torch.nn.functional.pad(torch.tensor(t),(0,l-len(t)),mode='constant',value=0) for t in v]),\n",
" torch.tensor(len_seq)\n",
" )\n",
"\n",
"train_loader_len = torch.utils.data.DataLoader(train_dataset, batch_size=16, collate_fn=pad_length, shuffle=True)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Реалната мрежа би била много подобна на `LSTMClassifier` по-горе, но `forward` преминаването ще получава както запълнения минипакет, така и вектора с дължините на последователностите. След изчисляване на вграждането, изчисляваме опакованата последователност, подаваме я на LSTM слоя и след това разопаковаме резултата обратно.\n",
"\n",
"> **Забележка**: Всъщност не използваме разопакования резултат `x`, защото използваме изхода от скритите слоеве в следващите изчисления. Следователно, можем изцяло да премахнем разопаковането от този код. Причината, поради която го поставяме тук, е за да можете лесно да модифицирате този код, в случай че се наложи да използвате изхода на мрежата в по-нататъшни изчисления.\n"
]
},
{
"cell_type": "code",
"execution_count": 7,
"metadata": {},
"outputs": [],
"source": [
"class LSTMPackClassifier(torch.nn.Module):\n",
" def __init__(self, vocab_size, embed_dim, hidden_dim, num_class):\n",
" super().__init__()\n",
" self.hidden_dim = hidden_dim\n",
" self.embedding = torch.nn.Embedding(vocab_size, embed_dim)\n",
" self.embedding.weight.data = torch.randn_like(self.embedding.weight.data)-0.5\n",
" self.rnn = torch.nn.LSTM(embed_dim,hidden_dim,batch_first=True)\n",
" self.fc = torch.nn.Linear(hidden_dim, num_class)\n",
"\n",
" def forward(self, x, lengths):\n",
" batch_size = x.size(0)\n",
" x = self.embedding(x)\n",
" pad_x = torch.nn.utils.rnn.pack_padded_sequence(x,lengths,batch_first=True,enforce_sorted=False)\n",
" pad_x,(h,c) = self.rnn(pad_x)\n",
" x, _ = torch.nn.utils.rnn.pad_packed_sequence(pad_x,batch_first=True)\n",
" return self.fc(h[-1])"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": []
},
{
"cell_type": "code",
"execution_count": 8,
"metadata": {
"scrolled": true
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"3200: acc=0.285625\n",
"6400: acc=0.33359375\n",
"9600: acc=0.3876041666666667\n",
"12800: acc=0.44078125\n",
"16000: acc=0.4825\n",
"19200: acc=0.5235416666666667\n",
"22400: acc=0.5559821428571429\n",
"25600: acc=0.58609375\n",
"28800: acc=0.6116666666666667\n",
"32000: acc=0.63340625\n",
"35200: acc=0.6525284090909091\n",
"38400: acc=0.668515625\n",
"41600: acc=0.6822596153846154\n",
"44800: acc=0.6948214285714286\n",
"48000: acc=0.7052708333333333\n",
"51200: acc=0.71521484375\n",
"54400: acc=0.7239889705882353\n",
"57600: acc=0.7315277777777778\n",
"60800: acc=0.7388486842105263\n",
"64000: acc=0.74571875\n",
"67200: acc=0.7518303571428572\n",
"70400: acc=0.7576988636363636\n",
"73600: acc=0.7628940217391305\n",
"76800: acc=0.7681510416666667\n",
"80000: acc=0.7728125\n",
"83200: acc=0.7772235576923077\n",
"86400: acc=0.7815393518518519\n",
"89600: acc=0.7857700892857142\n",
"92800: acc=0.7895043103448276\n",
"96000: acc=0.7930520833333333\n",
"99200: acc=0.7959072580645161\n",
"102400: acc=0.798994140625\n",
"105600: acc=0.802064393939394\n",
"108800: acc=0.8051378676470589\n",
"112000: acc=0.8077857142857143\n",
"115200: acc=0.8104600694444445\n",
"118400: acc=0.8128293918918919\n"
]
},
{
"data": {
"text/plain": [
"(0.029785829671223958, 0.8138166666666666)"
]
},
"execution_count": 8,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"net = LSTMPackClassifier(vocab_size,64,32,len(classes)).to(device)\n",
"train_epoch_emb(net,train_loader_len, lr=0.001,use_pack_sequence=True)\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"> **Забележка:** Може би сте забелязали параметъра `use_pack_sequence`, който предаваме на тренировъчната функция. В момента функцията `pack_padded_sequence` изисква дължината на последователността да бъде на CPU устройство, и затова тренировъчната функция трябва да избегне преместването на данните за дължината на последователността към GPU по време на обучението. Можете да разгледате имплементацията на функцията `train_emb` във файла [`torchnlp.py`](../../../../../lessons/5-NLP/16-RNN/torchnlp.py).\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Двунаправлени и многослойни RNN\n",
"\n",
"В нашите примери всички рекурентни мрежи работят в една посока, от началото на последователността до края. Това изглежда естествено, защото наподобява начина, по който четем и слушаме реч. Въпреки това, в много практически случаи имаме произволен достъп до входната последователност, което може да направи смислено изпълнението на рекурентни изчисления в двете посоки. Такива мрежи се наричат **двунаправлени** RNN, и те могат да бъдат създадени чрез задаване на параметъра `bidirectional=True` в конструктора на RNN/LSTM/GRU.\n",
"\n",
"Когато работим с двунаправлена мрежа, ще ни трябват два вектора за скрито състояние, по един за всяка посока. PyTorch кодира тези вектори като един вектор с два пъти по-голям размер, което е доста удобно, защото обикновено подавате полученото скрито състояние към напълно свързан линеен слой и просто трябва да вземете предвид това увеличение в размера при създаването на слоя.\n",
"\n",
"Рекурентната мрежа, независимо дали е еднопосочна или двунаправлена, улавя определени модели в рамките на последователността и може да ги съхранява в скрития вектор или да ги предава към изхода. Както при конволюционните мрежи, можем да изградим друг рекурентен слой върху първия, за да уловим модели на по-високо ниво, изградени от модели на ниско ниво, извлечени от първия слой. Това ни води до понятието **многослойна RNN**, която се състои от две или повече рекурентни мрежи, където изходът на предишния слой се подава като вход към следващия слой.\n",
"\n",
"![Изображение, показващо многослойна дългосрочно-краткосрочна памет RNN](../../../../../translated_images/multi-layer-lstm.dd975e29bb2a59fe58b429db833932d734c81f211cad2783797a9608984acb8c.bg.jpg)\n",
"\n",
"*Снимка от [този чудесен пост](https://towardsdatascience.com/from-a-lstm-cell-to-a-multilayer-lstm-network-with-pytorch-2899eb5696f3) от Фернандо Лопес*\n",
"\n",
"PyTorch прави изграждането на такива мрежи лесна задача, защото просто трябва да зададете параметъра `num_layers` в конструктора на RNN/LSTM/GRU, за да създадете автоматично няколко слоя рекурентност. Това също означава, че размерът на скрития/състояния вектор ще се увеличи пропорционално и ще трябва да вземете това предвид при обработката на изхода от рекурентните слоеве.\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## RNNs за други задачи\n",
"\n",
"В този раздел видяхме, че RNNs могат да се използват за класификация на последователности, но всъщност те могат да се справят с много повече задачи, като генериране на текст, машинен превод и други. Ще разгледаме тези задачи в следващия раздел.\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"\n---\n\n**Отказ от отговорност**: \nТози документ е преведен с помощта на AI услуга за превод [Co-op Translator](https://github.com/Azure/co-op-translator). Въпреки че се стремим към точност, моля, имайте предвид, че автоматизираните преводи може да съдържат грешки или неточности. Оригиналният документ на неговия роден език трябва да се счита за авторитетен източник. За критична информация се препоръчва професионален човешки превод. Ние не носим отговорност за недоразумения или погрешни интерпретации, произтичащи от използването на този превод.\n"
]
}
],
"metadata": {
"interpreter": {
"hash": "16af2a8bbb083ea23e5e41c7f5787656b2ce26968575d8763f2c4b17f9cd711f"
},
"kernelspec": {
"display_name": "Python 3.8.12 ('py38')",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.8.12"
},
"coopTranslator": {
"original_hash": "522ee52ae3d5ae933e283286254e9a55",
"translation_date": "2025-08-30T01:00:20+00:00",
"source_file": "lessons/5-NLP/16-RNN/RNNPyTorch.ipynb",
"language_code": "bg"
}
},
"nbformat": 4,
"nbformat_minor": 2
}

View File

@ -0,0 +1,460 @@
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Рекурентни невронни мрежи\n",
"\n",
"В предишния модул разгледахме богати семантични представяния на текст. Архитектурата, която използвахме, улавя обобщеното значение на думите в изречение, но не отчита **реда** на думите, тъй като операцията по обобщаване, която следва вгражданията, премахва тази информация от оригиналния текст. Тъй като тези модели не могат да представят реда на думите, те не могат да решават по-сложни или двусмислени задачи като генериране на текст или отговаряне на въпроси.\n",
"\n",
"За да уловим значението на текстова последователност, ще използваме архитектура на невронна мрежа, наречена **рекурентна невронна мрежа** или RNN. Когато използваме RNN, подаваме изречението си през мрежата, една по една дума (токен), и мрежата произвежда някакво **състояние**, което след това подаваме отново на мрежата заедно със следващия токен.\n",
"\n",
"![Изображение, показващо пример за генериране с рекурентна невронна мрежа.](../../../../../translated_images/rnn.27f5c29c53d727b546ad3961637a267f0fe9ec5ab01f2a26a853c92fcefbb574.bg.png)\n",
"\n",
"Дадена входна последователност от токени $X_0,\\dots,X_n$, RNN създава последователност от блокове на невронната мрежа и обучава тази последователност от край до край, използвайки обратно разпространение. Всеки блок на мрежата приема двойка $(X_i,S_i)$ като вход и произвежда $S_{i+1}$ като резултат. Финалното състояние $S_n$ или изходът $Y_n$ се подава към линеен класификатор, за да се произведе резултатът. Всички блокове на мрежата споделят едни и същи тегла и се обучават от край до край с един цикъл на обратно разпространение.\n",
"\n",
"> Фигурата по-горе показва рекурентна невронна мрежа в разгъната форма (вляво) и в по-компактна рекурентна форма (вдясно). Важно е да се разбере, че всички клетки на RNN имат едни и същи **споделени тегла**.\n",
"\n",
"Тъй като векторите на състоянието $S_0,\\dots,S_n$ преминават през мрежата, RNN може да научи последователни зависимости между думите. Например, когато думата *не* се появи някъде в последователността, мрежата може да научи да отрича определени елементи в рамките на вектора на състоянието.\n",
"\n",
"Вътре всяка клетка на RNN съдържа две матрици на тегла: $W_H$ и $W_I$, и отклонение $b$. На всяка стъпка на RNN, при дадени вход $X_i$ и входно състояние $S_i$, изходното състояние се изчислява като $S_{i+1} = f(W_H\\times S_i + W_I\\times X_i+b)$, където $f$ е функция на активация (често $\\tanh$).\n",
"\n",
"> За задачи като генериране на текст (които ще разгледаме в следващия модул) или машинен превод, също искаме да получим някаква изходна стойност на всяка стъпка на RNN. В този случай има и друга матрица $W_O$, а изходът се изчислява като $Y_i=f(W_O\\times S_i+b_O)$.\n",
"\n",
"Нека видим как рекурентните невронни мрежи могат да ни помогнат да класифицираме нашия набор от новини.\n",
"\n",
"> За пясъчната среда трябва да изпълним следващата клетка, за да се уверим, че необходимата библиотека е инсталирана и данните са предварително заредени. Ако работите локално, можете да пропуснете следващата клетка.\n"
]
},
{
"cell_type": "code",
"execution_count": 1,
"metadata": {},
"outputs": [],
"source": [
"import sys\n",
"!{sys.executable} -m pip install --quiet tensorflow_datasets==4.4.0\n",
"!cd ~ && wget -q -O - https://mslearntensorflowlp.blob.core.windows.net/data/tfds-ag-news.tgz | tar xz"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {},
"outputs": [],
"source": [
"import tensorflow as tf\n",
"from tensorflow import keras\n",
"import tensorflow_datasets as tfds\n",
"import numpy as np\n",
"\n",
"# We are going to be training pretty large models. In order not to face errors, we need\n",
"# to set tensorflow option to grow GPU memory allocation when required\n",
"physical_devices = tf.config.list_physical_devices('GPU') \n",
"if len(physical_devices)>0:\n",
" tf.config.experimental.set_memory_growth(physical_devices[0], True)\n",
"\n",
"ds_train, ds_test = tfds.load('ag_news_subset').values()"
]
},
{
"cell_type": "markdown",
"metadata": {
"nteract": {
"transient": {
"deleting": false
}
}
},
"source": [
"Когато обучавате големи модели, разпределението на паметта на GPU може да се превърне в проблем. Също така може да се наложи да експериментирате с различни размери на минипартидите, така че данните да се поберат в паметта на GPU, но обучението да бъде достатъчно бързо. Ако изпълнявате този код на собствена машина с GPU, можете да експериментирате с настройването на размера на минипартидите, за да ускорите обучението.\n",
"\n",
"> **Note**: Известно е, че определени версии на драйверите на NVidia не освобождават паметта след обучението на модела. В този тетрадка изпълняваме няколко примера, което може да доведе до изчерпване на паметта в определени конфигурации, особено ако правите собствени експерименти в рамките на същата тетрадка. Ако срещнете странни грешки при стартиране на обучението на модела, може да се наложи да рестартирате ядрото на тетрадката.\n"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {
"collapsed": true,
"jupyter": {
"outputs_hidden": false,
"source_hidden": false
},
"nteract": {
"transient": {
"deleting": false
}
}
},
"outputs": [],
"source": [
"batch_size = 16\n",
"embed_size = 64"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Прост класификатор с RNN\n",
"\n",
"При използването на прост RNN, всяка рекурентна единица представлява проста линейна мрежа, която приема входен вектор и вектор на състоянието и произвежда нов вектор на състоянието. В Keras това може да бъде представено чрез слоя `SimpleRNN`.\n",
"\n",
"Въпреки че можем директно да подаваме токени, кодирани с one-hot, към слоя RNN, това не е добра идея заради тяхната висока размерност. Затова ще използваме слой за вграждане (embedding layer), за да намалим размерността на векторите на думите, следван от слой RNN и накрая класификатор `Dense`.\n",
"\n",
"> **Note**: В случаи, когато размерността не е толкова висока, например при използване на токенизация на ниво символи, може да има смисъл да се подават токени, кодирани с one-hot, директно в клетката на RNN.\n"
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Model: \"sequential\"\n",
"_________________________________________________________________\n",
"Layer (type) Output Shape Param # \n",
"=================================================================\n",
"text_vectorization (TextVect (None, None) 0 \n",
"_________________________________________________________________\n",
"embedding (Embedding) (None, None, 64) 1280000 \n",
"_________________________________________________________________\n",
"simple_rnn (SimpleRNN) (None, 16) 1296 \n",
"_________________________________________________________________\n",
"dense (Dense) (None, 4) 68 \n",
"=================================================================\n",
"Total params: 1,281,364\n",
"Trainable params: 1,281,364\n",
"Non-trainable params: 0\n",
"_________________________________________________________________\n"
]
}
],
"source": [
"vocab_size = 20000\n",
"\n",
"vectorizer = keras.layers.experimental.preprocessing.TextVectorization(\n",
" max_tokens=vocab_size,\n",
" input_shape=(1,))\n",
"\n",
"model = keras.models.Sequential([\n",
" vectorizer,\n",
" keras.layers.Embedding(vocab_size, embed_size),\n",
" keras.layers.SimpleRNN(16),\n",
" keras.layers.Dense(4,activation='softmax')\n",
"])\n",
"\n",
"model.summary()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"> **Забележка:** Тук използваме нетрениран слой за вграждане за простота, но за по-добри резултати можем да използваме предварително обучен слой за вграждане с Word2Vec, както беше описано в предишния модул. Би било добра практика за вас да адаптирате този код, за да работи с предварително обучени вграждания.\n",
"\n",
"Сега нека обучим нашата RNN. RNN моделите като цяло са доста трудни за обучение, защото когато клетките на RNN се разгънат по дължината на последователността, броят на слоевете, участващи в обратното разпространение, става доста голям. Затова трябва да изберем по-малка скорост на обучение и да тренираме мрежата върху по-голям набор от данни, за да постигнем добри резултати. Това може да отнеме доста време, така че използването на GPU е за предпочитане.\n",
"\n",
"За да ускорим процеса, ще обучим RNN модела само върху заглавия на новини, като пропуснем описанията. Можете да опитате да тренирате с описания и да видите дали ще успеете да накарате модела да се обучи.\n"
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {
"scrolled": true
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Training vectorizer\n"
]
}
],
"source": [
"def extract_title(x):\n",
" return x['title']\n",
"\n",
"def tupelize_title(x):\n",
" return (extract_title(x),x['label'])\n",
"\n",
"print('Training vectorizer')\n",
"vectorizer.adapt(ds_train.take(2000).map(extract_title))"
]
},
{
"cell_type": "code",
"execution_count": 6,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"7500/7500 [==============================] - 82s 11ms/step - loss: 0.6629 - acc: 0.7623 - val_loss: 0.5559 - val_acc: 0.7995\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\n"
]
},
{
"data": {
"text/plain": [
"<tensorflow.python.keras.callbacks.History at 0x7f3e0030d350>"
]
},
"execution_count": 6,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"model.compile(loss='sparse_categorical_crossentropy',metrics=['acc'], optimizer='adam')\n",
"model.fit(ds_train.map(tupelize_title).batch(batch_size),validation_data=ds_test.map(tupelize_title).batch(batch_size))"
]
},
{
"cell_type": "markdown",
"metadata": {
"nteract": {
"transient": {
"deleting": false
}
}
},
"source": [
"> **Забележка** че точността вероятно ще бъде по-ниска тук, защото обучаваме само върху заглавия на новини.\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Преразглеждане на последователности с променлива дължина\n",
"\n",
"Не забравяйте, че слоят `TextVectorization` автоматично ще допълва последователностите с променлива дължина в минипартида с помощта на специални символи за запълване. Оказва се, че тези символи също участват в обучението и могат да усложнят сближаването на модела.\n",
"\n",
"Има няколко подхода, които можем да използваме, за да минимизираме количеството запълване. Един от тях е да пренаредим набора от данни по дължина на последователността и да групираме всички последователности по размер. Това може да се направи с помощта на функцията `tf.data.experimental.bucket_by_sequence_length` (вижте [документацията](https://www.tensorflow.org/api_docs/python/tf/data/experimental/bucket_by_sequence_length)).\n",
"\n",
"Друг подход е използването на **маскиране**. В Keras някои слоеве поддържат допълнителен вход, който показва кои символи трябва да се вземат предвид по време на обучението. За да включим маскирането в нашия модел, можем или да добавим отделен слой `Masking` ([документация](https://keras.io/api/layers/core_layers/masking/)), или да зададем параметъра `mask_zero=True` на нашия слой `Embedding`.\n",
"\n",
"> **Note**: Това обучение ще отнеме около 5 минути за завършване на една епоха върху целия набор от данни. Можете да прекъснете обучението по всяко време, ако загубите търпение. Също така можете да ограничите количеството данни, използвани за обучение, като добавите `.take(...)` след наборите от данни `ds_train` и `ds_test`.\n"
]
},
{
"cell_type": "code",
"execution_count": 7,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"7500/7500 [==============================] - 371s 49ms/step - loss: 0.5401 - acc: 0.8079 - val_loss: 0.3780 - val_acc: 0.8822\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\n"
]
},
{
"data": {
"text/plain": [
"<tensorflow.python.keras.callbacks.History at 0x7f3dec118850>"
]
},
"execution_count": 7,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"def extract_text(x):\n",
" return x['title']+' '+x['description']\n",
"\n",
"def tupelize(x):\n",
" return (extract_text(x),x['label'])\n",
"\n",
"model = keras.models.Sequential([\n",
" vectorizer,\n",
" keras.layers.Embedding(vocab_size,embed_size,mask_zero=True),\n",
" keras.layers.SimpleRNN(16),\n",
" keras.layers.Dense(4,activation='softmax')\n",
"])\n",
"\n",
"model.compile(loss='sparse_categorical_crossentropy',metrics=['acc'], optimizer='adam')\n",
"model.fit(ds_train.map(tupelize).batch(batch_size),validation_data=ds_test.map(tupelize).batch(batch_size))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Сега, когато използваме маскиране, можем да обучим модела върху целия набор от заглавия и описания.\n",
"\n",
"> **Note**: Забелязахте ли, че използваме векторизатор, обучен върху заглавията на новините, а не върху цялото съдържание на статията? Потенциално това може да доведе до игнориране на някои от токените, така че е по-добре да се преобучи векторизаторът. Въпреки това, ефектът може да е много малък, затова ще се придържаме към предварително обучения векторизатор за по-голяма простота.\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## LSTM: Дългосрочна краткосрочна памет\n",
"\n",
"Един от основните проблеми на RNN е **изчезващите градиенти**. RNN могат да бъдат доста дълги и може да им е трудно да пренасят градиентите обратно до първия слой на мрежата по време на обратното разпространение. Когато това се случи, мрежата не може да научи връзките между отдалечени токени. Един от начините за избягване на този проблем е въвеждането на **явно управление на състоянието** чрез използване на **гейтове**. Двете най-често срещани архитектури, които използват гейтове, са **дългосрочна краткосрочна памет** (LSTM) и **гейтирана релейна единица** (GRU). Тук ще разгледаме LSTM.\n",
"\n",
"![Изображение, показващо пример за клетка с дългосрочна краткосрочна памет](../../../../../lessons/5-NLP/16-RNN/images/long-short-term-memory-cell.svg)\n",
"\n",
"LSTM мрежата е организирана по начин, подобен на RNN, но има две състояния, които се предават от слой на слой: действителното състояние $c$ и скритият вектор $h$. Във всяка единица скритият вектор $h_{t-1}$ се комбинира с входа $x_t$, и заедно те контролират какво се случва със състоянието $c_t$ и изхода $h_{t}$ чрез **гейтове**. Всеки гейт има сигмоидна активация (изход в диапазона $[0,1]$), която може да се разглежда като побитова маска, когато се умножи със състоянието. LSTM имат следните гейтове (отляво надясно на изображението по-горе):\n",
"* **гейт за забравяне**, който определя кои компоненти на вектора $c_{t-1}$ трябва да забравим и кои да предадем нататък.\n",
"* **входен гейт**, който определя колко информация от входния вектор и предишния скрит вектор трябва да бъде включена в състоянието.\n",
"* **изходен гейт**, който взема новото състояние и решава кои от неговите компоненти ще бъдат използвани за създаване на новия скрит вектор $h_t$.\n",
"\n",
"Компонентите на състоянието $c$ могат да се разглеждат като флагове, които могат да бъдат включвани и изключвани. Например, когато срещнем името *Алис* в последователността, предполагаме, че става въпрос за жена, и вдигаме флаг в състоянието, който казва, че имаме женско съществително в изречението. Когато по-нататък срещнем думите *и Том*, ще вдигнем флаг, който казва, че имаме множествено число. Така чрез манипулиране на състоянието можем да следим граматическите свойства на изречението.\n",
"\n",
"> **Note**: Ето един чудесен ресурс за разбиране на вътрешната структура на LSTM: [Understanding LSTM Networks](https://colah.github.io/posts/2015-08-Understanding-LSTMs/) от Кристофър Ола.\n",
"\n",
"Въпреки че вътрешната структура на LSTM клетката може да изглежда сложна, Keras скрива тази имплементация вътре в слоя `LSTM`, така че единственото, което трябва да направим в примера по-горе, е да заменим рекурентния слой:\n"
]
},
{
"cell_type": "code",
"execution_count": 8,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"15000/15000 [==============================] - 188s 13ms/step - loss: 0.5692 - acc: 0.7916 - val_loss: 0.3441 - val_acc: 0.8870\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\n"
]
},
{
"data": {
"text/plain": [
"<tensorflow.python.keras.callbacks.History at 0x7f3d6af5c350>"
]
},
"execution_count": 8,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"model = keras.models.Sequential([\n",
" vectorizer,\n",
" keras.layers.Embedding(vocab_size, embed_size),\n",
" keras.layers.LSTM(8),\n",
" keras.layers.Dense(4,activation='softmax')\n",
"])\n",
"\n",
"model.compile(loss='sparse_categorical_crossentropy',metrics=['acc'], optimizer='adam')\n",
"model.fit(ds_train.map(tupelize).batch(8),validation_data=ds_test.map(tupelize).batch(8))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": []
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Двунасочни и многослойни RNN\n",
"\n",
"В досегашните ни примери рекурентните мрежи работят от началото на последователността до края. Това ни се струва естествено, защото следва същата посока, в която четем или слушаме реч. Въпреки това, за сценарии, които изискват произволен достъп до входната последователност, е по-логично рекурентните изчисления да се извършват и в двете посоки. RNN, които позволяват изчисления в двете посоки, се наричат **двунасочни** RNN, и могат да бъдат създадени чрез обвиване на рекурентния слой със специален слой `Bidirectional`.\n",
"\n",
"> **Note**: Слоят `Bidirectional` прави две копия на слоя в него и задава свойството `go_backwards` на едно от тези копия на `True`, което го кара да се движи в обратната посока по последователността.\n",
"\n",
"Рекурентните мрежи, еднопосочни или двунасочни, улавят модели в рамките на последователността и ги съхраняват в състояния или ги връщат като изход. Както при конволюционните мрежи, можем да изградим друг рекурентен слой след първия, за да уловим модели на по-високо ниво, изградени от модели на по-ниско ниво, извлечени от първия слой. Това ни води до понятието за **многослойна RNN**, която се състои от две или повече рекурентни мрежи, където изходът на предишния слой се предава на следващия слой като вход.\n",
"\n",
"![Изображение, показващо многослойна дългосрочно-краткосрочна памет RNN](../../../../../translated_images/multi-layer-lstm.dd975e29bb2a59fe58b429db833932d734c81f211cad2783797a9608984acb8c.bg.jpg)\n",
"\n",
"*Снимка от [този чудесен пост](https://towardsdatascience.com/from-a-lstm-cell-to-a-multilayer-lstm-network-with-pytorch-2899eb5696f3) от Фернандо Лопес.*\n",
"\n",
"Keras прави изграждането на тези мрежи лесна задача, защото просто трябва да добавите повече рекурентни слоеве към модела. За всички слоеве, освен последния, трябва да зададем параметъра `return_sequences=True`, защото ни е необходимо слоят да връща всички междинни състояния, а не само крайното състояние на рекурентното изчисление.\n",
"\n",
"Нека изградим двуслоен двунасочен LSTM за нашия проблем с класификацията.\n",
"\n",
"> **Note** този код отново отнема доста време за изпълнение, но ни дава най-високата точност, която сме виждали досега. Така че може би си струва да изчакаме и да видим резултата.\n"
]
},
{
"cell_type": "code",
"execution_count": 9,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"5044/7500 [===================>..........] - ETA: 2:33 - loss: 0.3709 - acc: 0.8706\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\r5045/7500 [===================>..........] - ETA: 2:33 - loss: 0.3709 - acc: 0.8706"
]
}
],
"source": [
"model = keras.models.Sequential([\n",
" vectorizer,\n",
" keras.layers.Embedding(vocab_size, 128, mask_zero=True),\n",
" keras.layers.Bidirectional(keras.layers.LSTM(64,return_sequences=True)),\n",
" keras.layers.Bidirectional(keras.layers.LSTM(64)), \n",
" keras.layers.Dense(4,activation='softmax')\n",
"])\n",
"\n",
"model.compile(loss='sparse_categorical_crossentropy',metrics=['acc'], optimizer='adam')\n",
"model.fit(ds_train.map(tupelize).batch(batch_size),\n",
" validation_data=ds_test.map(tupelize).batch(batch_size))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## RNNs за други задачи\n",
"\n",
"Досега се концентрирахме върху използването на RNNs за класифициране на текстови последователности. Но те могат да се справят с много повече задачи, като генериране на текст и машинен превод — ще разгледаме тези задачи в следващата единица.\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"\n---\n\n**Отказ от отговорност**: \nТози документ е преведен с помощта на AI услуга за превод [Co-op Translator](https://github.com/Azure/co-op-translator). Въпреки че се стремим към точност, моля, имайте предвид, че автоматизираните преводи може да съдържат грешки или неточности. Оригиналният документ на неговия роден език трябва да се счита за авторитетен източник. За критична информация се препоръчва професионален човешки превод. Не носим отговорност за недоразумения или погрешни интерпретации, произтичащи от използването на този превод.\n"
]
}
],
"metadata": {
"kernel_info": {
"name": "conda-env-py37_tensorflow-py"
},
"kernelspec": {
"display_name": "py37_tensorflow",
"language": "python",
"name": "conda-env-py37_tensorflow-py"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.7.9"
},
"nteract": {
"version": "nteract-front-end@1.0.0"
},
"coopTranslator": {
"original_hash": "81351e61f619b432ff51010a4f993194",
"translation_date": "2025-08-30T00:56:44+00:00",
"source_file": "lessons/5-NLP/16-RNN/RNNTF.ipynb",
"language_code": "bg"
}
},
"nbformat": 4,
"nbformat_minor": 4
}

View File

@ -0,0 +1,414 @@
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Генеративни мрежи\n",
"\n",
"Рекурентните невронни мрежи (RNNs) и техните варианти с управлявани клетки, като клетки с дългосрочна памет (LSTMs) и управлявани рекурентни единици (GRUs), предоставят механизъм за моделиране на езика, т.е. те могат да научат подредбата на думите и да предсказват следващата дума в последователността. Това ни позволява да използваме RNNs за **генеративни задачи**, като обикновено генериране на текст, машинен превод и дори създаване на описания за изображения.\n",
"\n",
"В архитектурата на RNN, която обсъдихме в предишния модул, всяка RNN единица произвежда следващото скрито състояние като изход. Въпреки това, можем да добавим още един изход към всяка рекурентна единица, което би ни позволило да изведем **последователност** (която е равна по дължина на оригиналната последователност). Освен това, можем да използваме RNN единици, които не приемат вход на всяка стъпка, а просто вземат някакъв начален вектор на състоянието и след това произвеждат последователност от изходи.\n",
"\n",
"В този тетрадка ще се фокусираме върху прости генеративни модели, които ни помагат да генерираме текст. За простота, нека изградим **мрежа на ниво символи**, която генерира текст буква по буква. По време на обучението трябва да вземем някакъв текстов корпус и да го разделим на последователности от букви.\n"
]
},
{
"cell_type": "code",
"execution_count": 1,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Loading dataset...\n",
"Building vocab...\n"
]
}
],
"source": [
"import torch\n",
"import torchtext\n",
"import numpy as np\n",
"from torchnlp import *\n",
"train_dataset,test_dataset,classes,vocab = load_dataset()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Създаване на речник от символи\n",
"\n",
"За да изградим генеративна мрежа на ниво символи, трябва да разделим текста на отделни символи вместо на думи. Това може да се постигне чрез дефиниране на различен токенизатор:\n"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Vocabulary size = 82\n",
"Encoding of 'a' is 1\n",
"Character with code 13 is c\n"
]
}
],
"source": [
"def char_tokenizer(words):\n",
" return list(words) #[word for word in words]\n",
"\n",
"counter = collections.Counter()\n",
"for (label, line) in train_dataset:\n",
" counter.update(char_tokenizer(line))\n",
"vocab = torchtext.vocab.vocab(counter)\n",
"\n",
"vocab_size = len(vocab)\n",
"print(f\"Vocabulary size = {vocab_size}\")\n",
"print(f\"Encoding of 'a' is {vocab.get_stoi()['a']}\")\n",
"print(f\"Character with code 13 is {vocab.get_itos()[13]}\")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Нека видим примера за това как можем да кодираме текста от нашия набор от данни:\n"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"tensor([ 0, 1, 2, 2, 3, 4, 5, 6, 3, 7, 8, 1, 9, 10, 3, 11, 2, 1,\n",
" 12, 3, 7, 1, 13, 14, 3, 15, 16, 5, 17, 3, 5, 18, 8, 3, 7, 2,\n",
" 1, 13, 14, 3, 19, 20, 8, 21, 5, 8, 9, 10, 22, 3, 20, 8, 21, 5,\n",
" 8, 9, 10, 3, 23, 3, 4, 18, 17, 9, 5, 23, 10, 8, 2, 2, 8, 9,\n",
" 10, 24, 3, 0, 1, 2, 2, 3, 4, 5, 9, 8, 8, 5, 25, 10, 3, 26,\n",
" 12, 27, 16, 26, 2, 27, 16, 28, 29, 30, 1, 16, 26, 3, 17, 31, 3, 21,\n",
" 2, 5, 9, 1, 23, 13, 32, 16, 27, 13, 10, 24, 3, 1, 9, 8, 3, 10,\n",
" 8, 8, 27, 16, 28, 3, 28, 9, 8, 8, 16, 3, 1, 28, 1, 27, 16, 6])"
]
},
"execution_count": 3,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"def enc(x):\n",
" return torch.LongTensor(encode(x,voc=vocab,tokenizer=char_tokenizer))\n",
"\n",
"enc(train_dataset[0][1])"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Обучение на генеративна RNN\n",
"\n",
"Начинът, по който ще обучим RNN да генерира текст, е следният. На всяка стъпка ще вземем последователност от символи с дължина `nchars` и ще помолим мрежата да генерира следващия изходен символ за всеки входен символ:\n",
"\n",
"![Изображение, показващо пример за генериране на думата 'HELLO' от RNN.](../../../../../translated_images/rnn-generate.56c54afb52f9781d63a7c16ea9c1b86cb70e6e1eae6a742b56b7b37468576b17.bg.png)\n",
"\n",
"В зависимост от конкретния сценарий, може да искаме да включим и някои специални символи, като например *край на последователността* `<eos>`. В нашия случай просто искаме да обучим мрежата за безкрайно генериране на текст, затова ще фиксираме размера на всяка последователност да бъде равен на `nchars` токени. Следователно, всеки тренировъчен пример ще се състои от `nchars` входове и `nchars` изходи (които са входната последователност, изместена с един символ наляво). Минипартидата ще се състои от няколко такива последователности.\n",
"\n",
"Начинът, по който ще генерираме минипартиди, е да вземем всеки новинарски текст с дължина `l` и да генерираме всички възможни входно-изходни комбинации от него (ще има `l-nchars` такива комбинации). Те ще формират една минипартида, а размерът на минипартидите ще бъде различен на всяка тренировъчна стъпка.\n"
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"(tensor([[ 0, 1, 2, ..., 28, 29, 30],\n",
" [ 1, 2, 2, ..., 29, 30, 1],\n",
" [ 2, 2, 3, ..., 30, 1, 16],\n",
" ...,\n",
" [20, 8, 21, ..., 1, 28, 1],\n",
" [ 8, 21, 5, ..., 28, 1, 27],\n",
" [21, 5, 8, ..., 1, 27, 16]]),\n",
" tensor([[ 1, 2, 2, ..., 29, 30, 1],\n",
" [ 2, 2, 3, ..., 30, 1, 16],\n",
" [ 2, 3, 4, ..., 1, 16, 26],\n",
" ...,\n",
" [ 8, 21, 5, ..., 28, 1, 27],\n",
" [21, 5, 8, ..., 1, 27, 16],\n",
" [ 5, 8, 9, ..., 27, 16, 6]]))"
]
},
"execution_count": 4,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"nchars = 100\n",
"\n",
"def get_batch(s,nchars=nchars):\n",
" ins = torch.zeros(len(s)-nchars,nchars,dtype=torch.long,device=device)\n",
" outs = torch.zeros(len(s)-nchars,nchars,dtype=torch.long,device=device)\n",
" for i in range(len(s)-nchars):\n",
" ins[i] = enc(s[i:i+nchars])\n",
" outs[i] = enc(s[i+1:i+nchars+1])\n",
" return ins,outs\n",
"\n",
"get_batch(train_dataset[0][1])"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Сега нека дефинираме генераторната мрежа. Тя може да бъде базирана на всяка рекурентна клетка, която обсъдихме в предишния модул (обикновена, LSTM или GRU). В нашия пример ще използваме LSTM.\n",
"\n",
"Тъй като мрежата приема символи като вход, а размерът на речника е сравнително малък, нямаме нужда от embedding слой входът, кодиран като one-hot, може директно да бъде подаден към LSTM клетката. Въпреки това, понеже подаваме номера на символите като вход, трябва да ги кодираме като one-hot, преди да ги предадем на LSTM. Това се извършва чрез извикване на функцията `one_hot` по време на `forward` преминаването. Енкодерът на изхода ще бъде линеен слой, който ще преобразува скритото състояние в изход, кодиран като one-hot.\n"
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {},
"outputs": [],
"source": [
"class LSTMGenerator(torch.nn.Module):\n",
" def __init__(self, vocab_size, hidden_dim):\n",
" super().__init__()\n",
" self.rnn = torch.nn.LSTM(vocab_size,hidden_dim,batch_first=True)\n",
" self.fc = torch.nn.Linear(hidden_dim, vocab_size)\n",
"\n",
" def forward(self, x, s=None):\n",
" x = torch.nn.functional.one_hot(x,vocab_size).to(torch.float32)\n",
" x,s = self.rnn(x,s)\n",
" return self.fc(x),s"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"По време на обучението искаме да можем да извадим примерен генериран текст. За да направим това, ще дефинираме функцията `generate`, която ще произвежда изходен низ с дължина `size`, започвайки от началния низ `start`.\n",
"\n",
"Ето как работи. Първо, ще предадем целия начален низ през мрежата и ще вземем изходното състояние `s` и следващия предсказан символ `out`. Тъй като `out` е едно-горещо кодиран, използваме `argmax`, за да получим индекса на символа `nc` в речника, и използваме `itos`, за да определим действителния символ и да го добавим към резултатния списък от символи `chars`. Този процес на генериране на един символ се повтаря `size` пъти, за да се генерира необходимият брой символи.\n"
]
},
{
"cell_type": "code",
"execution_count": 8,
"metadata": {},
"outputs": [],
"source": [
"def generate(net,size=100,start='today '):\n",
" chars = list(start)\n",
" out, s = net(enc(chars).view(1,-1).to(device))\n",
" for i in range(size):\n",
" nc = torch.argmax(out[0][-1])\n",
" chars.append(vocab.get_itos()[nc])\n",
" out, s = net(nc.view(1,-1),s)\n",
" return ''.join(chars)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Сега да започнем обучението! Цикълът за обучение е почти същият като в предишните ни примери, но вместо точност, отпечатваме генериран текст на всеки 1000 епохи.\n",
"\n",
"Особено внимание трябва да се обърне на начина, по който изчисляваме загубата. Трябва да изчислим загубата, като използваме едно-горещо-кодирания изход `out` и очаквания текст `text_out`, който е списък с индекси на символи. За щастие, функцията `cross_entropy` очаква ненормализиран изход от мрежата като първи аргумент и номер на класа като втори, което е точно това, което имаме. Тя също така автоматично извършва осредняване спрямо размера на минипартидата.\n",
"\n",
"Ограничаваме обучението до `samples_to_train` проби, за да не чакаме прекалено дълго. Насърчаваме ви да експериментирате и да опитате по-дълго обучение, възможно за няколко епохи (в такъв случай ще трябва да създадете друг цикъл около този код).\n"
]
},
{
"cell_type": "code",
"execution_count": 9,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Current loss = 4.398899078369141\n",
"today sr sr sr sr sr sr sr sr sr sr sr sr sr sr sr sr sr sr sr sr sr sr sr sr sr sr sr sr sr sr sr sr sr s\n",
"Current loss = 2.161320447921753\n",
"today and to the tor to to the tor to to the tor to to the tor to to the tor to to the tor to to the tor t\n",
"Current loss = 1.6722588539123535\n",
"today and the court to the could to the could to the could to the could to the could to the could to the c\n",
"Current loss = 2.423795223236084\n",
"today and a second to the conternation of the conternation of the conternation of the conternation of the \n",
"Current loss = 1.702607274055481\n",
"today and the company to the company to the company to the company to the company to the company to the co\n",
"Current loss = 1.692358136177063\n",
"today and the company to the company to the company to the company to the company to the company to the co\n",
"Current loss = 1.9722288846969604\n",
"today and the control the control the control the control the control the control the control the control \n",
"Current loss = 1.8705692291259766\n",
"today and the second to the second to the second to the second to the second to the second to the second t\n",
"Current loss = 1.7626899480819702\n",
"today and a security and a security and a security and a security and a security and a security and a secu\n",
"Current loss = 1.5574463605880737\n",
"today and the company and the company and the company and the company and the company and the company and \n",
"Current loss = 1.5620026588439941\n",
"today and the be that the be the be that the be the be that the be the be that the be the be that the be t\n"
]
}
],
"source": [
"net = LSTMGenerator(vocab_size,64).to(device)\n",
"\n",
"samples_to_train = 10000\n",
"optimizer = torch.optim.Adam(net.parameters(),0.01)\n",
"loss_fn = torch.nn.CrossEntropyLoss()\n",
"net.train()\n",
"for i,x in enumerate(train_dataset):\n",
" # x[0] is class label, x[1] is text\n",
" if len(x[1])-nchars<10:\n",
" continue\n",
" samples_to_train-=1\n",
" if not samples_to_train: break\n",
" text_in, text_out = get_batch(x[1])\n",
" optimizer.zero_grad()\n",
" out,s = net(text_in)\n",
" loss = torch.nn.functional.cross_entropy(out.view(-1,vocab_size),text_out.flatten()) #cross_entropy(out,labels)\n",
" loss.backward()\n",
" optimizer.step()\n",
" if i%1000==0:\n",
" print(f\"Current loss = {loss.item()}\")\n",
" print(generate(net))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Този пример вече генерира доста добър текст, но може да бъде подобрен по няколко начина:\n",
"\n",
"* **По-добро генериране на минипартиди**. Начинът, по който подготвихме данните за обучение, беше да генерираме една минипартида от една проба. Това не е идеално, защото минипартидите са с различни размери, а някои от тях дори не могат да бъдат генерирани, защото текстът е по-малък от `nchars`. Освен това, малките минипартиди не натоварват достатъчно GPU. По-разумно би било да се вземе един голям блок текст от всички проби, след това да се генерират всички входно-изходни двойки, да се разбъркат и да се генерират минипартиди с равен размер.\n",
"\n",
"* **Многослоен LSTM**. Има смисъл да се опитат 2 или 3 слоя LSTM клетки. Както споменахме в предишния модул, всеки слой на LSTM извлича определени модели от текста, и в случая на генератор на ниво символи можем да очакваме, че по-ниското ниво на LSTM ще бъде отговорно за извличането на срички, а по-високите нива - за думи и комбинации от думи. Това може лесно да се реализира чрез подаване на параметър за броя на слоевете към конструктора на LSTM.\n",
"\n",
"* Може също да искате да експериментирате с **GRU единици** и да видите кои от тях се представят по-добре, както и с **различни размери на скритите слоеве**. Твърде голям скрит слой може да доведе до пренасищане (например мрежата ще научи точния текст), а по-малък размер може да не произведе добър резултат.\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Генериране на мек текст и температура\n",
"\n",
"В предишната дефиниция на `generate`, винаги избирахме символа с най-висока вероятност като следващ символ в генерирания текст. Това водеше до факта, че текстът често \"циклираше\" между едни и същи последователности от символи отново и отново, както в този пример:\n",
"```\n",
"today of the second the company and a second the company ...\n",
"```\n",
"\n",
"Обаче, ако погледнем разпределението на вероятностите за следващия символ, може да се окаже, че разликата между няколко от най-високите вероятности не е голяма, например един символ може да има вероятност 0.2, а друг - 0.19 и т.н. Например, когато търсим следващия символ в последователността '*play*', следващият символ може еднакво добре да бъде или интервал, или **e** (както в думата *player*).\n",
"\n",
"Това ни води до извода, че не винаги е \"справедливо\" да избираме символа с по-висока вероятност, защото избирането на втория по вероятност също може да доведе до смислен текст. По-разумно е да **извличаме на случаен принцип** символи от разпределението на вероятностите, дадено от изхода на мрежата.\n",
"\n",
"Това извличане може да се извърши с помощта на функцията `multinomial`, която реализира така нареченото **мултиномиално разпределение**. Функция, която реализира това **меко** генериране на текст, е дефинирана по-долу:\n"
]
},
{
"cell_type": "code",
"execution_count": 10,
"metadata": {
"scrolled": true
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"--- Temperature = 0.3\n",
"Today and a company and complete an all the land the restrational the as a security and has provers the pay to and a report and the computer in the stand has filities and working the law the stations for a company and with the company and the final the first company and refight of the state and and workin\n",
"\n",
"--- Temperature = 0.8\n",
"Today he oniis its first to Aus bomblaties the marmation a to manan boogot that pirate assaid a relaid their that goverfin the the Cappets Ecrotional Assonia Cition targets it annight the w scyments Blamity #39;s TVeer Diercheg Reserals fran envyuil that of ster said access what succers of Dour-provelith\n",
"\n",
"--- Temperature = 1.0\n",
"Today holy they a 11 will meda a toket subsuaties, engins for Chanos, they's has stainger past to opening orital his thempting new Nattona was al innerforder advan-than #36;s night year his religuled talitatian what the but with Wednesday to Justment will wemen of Mark CCC Camp as Timed Nae wome a leaders\n",
"\n",
"--- Temperature = 1.3\n",
"Today gpone 2.5 fech atcusion poor cocles toparsdorM.cht Line Pamage put 43 his calt lowed to the book, that has authh-the silia rruch ailing to'ory andhes beutirsimi- Aefffive heading offil an auf eacklets is charged evis, Gunymy oy) Mony has it after-sloythyor loveId out filme, the Natabl -Najuntaxiggs \n",
"\n",
"--- Temperature = 1.8\n",
"Today plary, P.slan chly\\401 mardregationly #39;t 8.1Mide) closes ,filtcon alfly playin roven!\\grea.-QFBEP: Iss onfarchQ/itilia CCf Zivesigntwasta orce.-Peul-aw.uicrin of fuglinfsut aftaningwo, MIEX awayew Aice Woiduar Corvagiugge oppo esig ThusBratourid canthly-RyI.co lagitems\\eexciaishes.conBabntusmor I\n",
"\n"
]
}
],
"source": [
"def generate_soft(net,size=100,start='today ',temperature=1.0):\n",
" chars = list(start)\n",
" out, s = net(enc(chars).view(1,-1).to(device))\n",
" for i in range(size):\n",
" #nc = torch.argmax(out[0][-1])\n",
" out_dist = out[0][-1].div(temperature).exp()\n",
" nc = torch.multinomial(out_dist,1)[0]\n",
" chars.append(vocab.get_itos()[nc])\n",
" out, s = net(nc.view(1,-1),s)\n",
" return ''.join(chars)\n",
" \n",
"for i in [0.3,0.8,1.0,1.3,1.8]:\n",
" print(f\"--- Temperature = {i}\\n{generate_soft(net,size=300,start='Today ',temperature=i)}\\n\")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Въведохме още един параметър, наречен **температура**, който се използва, за да покаже колко строго трябва да се придържаме към най-високата вероятност. Ако температурата е 1.0, правим честно мултиномиално извадково вземане, а когато температурата достигне до безкрайност - всички вероятности стават равни и случайно избираме следващия символ. В примера по-долу можем да наблюдаваме, че текстът става безсмислен, когато увеличим температурата твърде много, и прилича на \"циклично\" твърдо генериран текст, когато се доближи до 0.\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"\n---\n\n**Отказ от отговорност**: \nТози документ е преведен с помощта на AI услуга за превод [Co-op Translator](https://github.com/Azure/co-op-translator). Въпреки че се стремим към точност, моля, имайте предвид, че автоматизираните преводи може да съдържат грешки или неточности. Оригиналният документ на неговия роден език трябва да се счита за авторитетен източник. За критична информация се препоръчва професионален човешки превод. Ние не носим отговорност за недоразумения или погрешни интерпретации, произтичащи от използването на този превод.\n"
]
}
],
"metadata": {
"interpreter": {
"hash": "16af2a8bbb083ea23e5e41c7f5787656b2ce26968575d8763f2c4b17f9cd711f"
},
"kernelspec": {
"display_name": "Python 3.8.12 ('py38')",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.8.12"
},
"coopTranslator": {
"original_hash": "7673cd150d96c74c6d6011460094efb4",
"translation_date": "2025-08-30T00:38:19+00:00",
"source_file": "lessons/5-NLP/17-GenerativeNetworks/GenerativePyTorch.ipynb",
"language_code": "bg"
}
},
"nbformat": 4,
"nbformat_minor": 4
}

View File

@ -0,0 +1,497 @@
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Генеративни мрежи\n",
"\n",
"Рекурентните невронни мрежи (RNNs) и техните варианти с управлявани клетки, като клетки с дългосрочна памет (LSTMs) и управлявани рекурентни единици (GRUs), предоставят механизъм за моделиране на език, т.е. те могат да научат подредбата на думите и да предсказват следващата дума в последователност. Това ни позволява да използваме RNNs за **генеративни задачи**, като обикновено генериране на текст, машинен превод и дори създаване на описания за изображения.\n",
"\n",
"В архитектурата на RNN, която обсъдихме в предишния модул, всяка RNN единица произвежда следващото скрито състояние като изход. Въпреки това, можем да добавим още един изход към всяка рекурентна единица, което би ни позволило да изведем **последователност** (която е равна по дължина на оригиналната последователност). Освен това, можем да използваме RNN единици, които не приемат вход на всяка стъпка, а просто вземат някакъв начален вектор на състояние и след това произвеждат последователност от изходи.\n",
"\n",
"В този тетраден файл ще се фокусираме върху прости генеративни модели, които ни помагат да генерираме текст. За простота, нека изградим **мрежа на ниво символи**, която генерира текст буква по буква. По време на обучението трябва да вземем някакъв текстов корпус и да го разделим на последователности от букви.\n"
]
},
{
"cell_type": "code",
"execution_count": 1,
"metadata": {},
"outputs": [],
"source": [
"import tensorflow as tf\n",
"from tensorflow import keras\n",
"import tensorflow_datasets as tfds\n",
"import numpy as np\n",
"\n",
"ds_train, ds_test = tfds.load('ag_news_subset').values()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Създаване на речник от символи\n",
"\n",
"За да изградим генеративна мрежа на ниво символи, трябва да разделим текста на отделни символи вместо на думи. `TextVectorization` слой, който използвахме преди, не може да направи това, затова имаме две опции:\n",
"\n",
"* Ръчно зареждане на текста и извършване на токенизация „на ръка“, както е показано в [този официален пример на Keras](https://keras.io/examples/generative/lstm_character_level_text_generation/)\n",
"* Използване на класа `Tokenizer` за токенизация на ниво символи.\n",
"\n",
"Ще изберем втория вариант. `Tokenizer` може да се използва и за токенизация на ниво думи, така че лесно може да се премине от токенизация на ниво символи към токенизация на ниво думи.\n",
"\n",
"За да извършим токенизация на ниво символи, трябва да предадем параметъра `char_level=True`:\n"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {},
"outputs": [],
"source": [
"def extract_text(x):\n",
" return x['title']+' '+x['description']\n",
"\n",
"def tupelize(x):\n",
" return (extract_text(x),x['label'])\n",
"\n",
"tokenizer = keras.preprocessing.text.Tokenizer(char_level=True,lower=False)\n",
"tokenizer.fit_on_texts([x['title'].numpy().decode('utf-8') for x in ds_train])"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Искаме също да използваме един специален токен, за да обозначим **края на последователността**, който ще наречем `<eos>`. Нека го добавим ръчно към речника:\n"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {},
"outputs": [],
"source": [
"eos_token = len(tokenizer.word_index)+1\n",
"tokenizer.word_index['<eos>'] = eos_token\n",
"\n",
"vocab_size = eos_token + 1"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Сега, за да кодираме текст в последователности от числа, можем да използваме:\n"
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"[[48, 2, 10, 10, 5, 44, 1, 25, 5, 8, 10, 13, 78]]"
]
},
"execution_count": 4,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"tokenizer.texts_to_sequences(['Hello, world!'])"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Обучение на генеративна RNN за създаване на заглавия\n",
"\n",
"Начинът, по който ще обучим RNN да генерира новинарски заглавия, е следният. На всяка стъпка ще вземем едно заглавие, което ще бъде подадено в RNN, и за всеки входен символ ще поискаме от мрежата да генерира следващия изходен символ:\n",
"\n",
"![Изображение, показващо пример за генериране на думата 'HELLO' с RNN.](../../../../../translated_images/rnn-generate.56c54afb52f9781d63a7c16ea9c1b86cb70e6e1eae6a742b56b7b37468576b17.bg.png)\n",
"\n",
"За последния символ от нашата последователност ще поискаме от мрежата да генерира токен `<eos>`.\n",
"\n",
"Основната разлика при генеративната RNN, която използваме тук, е, че ще вземем изхода от всяка стъпка на RNN, а не само от последната клетка. Това може да се постигне чрез задаване на параметъра `return_sequences` на RNN клетката.\n",
"\n",
"Така, по време на обучението, входът към мрежата ще бъде последователност от кодирани символи с определена дължина, а изходът ще бъде последователност със същата дължина, но изместена с един елемент и завършваща с `<eos>`. Минипакетът ще се състои от няколко такива последователности, и ще трябва да използваме **допълване** (padding), за да изравним всички последователности.\n",
"\n",
"Нека създадем функции, които ще трансформират набора от данни за нас. Тъй като искаме да допълваме последователностите на ниво минипакет, първо ще групираме набора от данни, като извикаме `.batch()`, а след това ще използваме `map`, за да извършим трансформацията. Следователно, функцията за трансформация ще приема цял минипакет като параметър:\n"
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {},
"outputs": [],
"source": [
"def title_batch(x):\n",
" x = [t.numpy().decode('utf-8') for t in x]\n",
" z = tokenizer.texts_to_sequences(x)\n",
" z = tf.keras.preprocessing.sequence.pad_sequences(z)\n",
" return tf.one_hot(z,vocab_size), tf.one_hot(tf.concat([z[:,1:],tf.constant(eos_token,shape=(len(z),1))],axis=1),vocab_size)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Няколко важни неща, които правим тук:\n",
"* Първо извличаме действителния текст от низовия тензор\n",
"* `text_to_sequences` преобразува списъка от низове в списък от цели тензори\n",
"* `pad_sequences` допълва тези тензори до тяхната максимална дължина\n",
"* Накрая извършваме еднократно кодиране на всички символи, както и изместване и добавяне на `<eos>`. Скоро ще видим защо ни трябват еднократно кодирани символи\n",
"\n",
"Въпреки това, тази функция е **Pythonic**, т.е. тя не може автоматично да бъде преведена в изчислителен граф на Tensorflow. Ще получим грешки, ако се опитаме да използваме тази функция директно в `Dataset.map` функцията. Трябва да обгърнем този Pythonic повик, като използваме обвивката `py_function`:\n"
]
},
{
"cell_type": "code",
"execution_count": 6,
"metadata": {},
"outputs": [],
"source": [
"def title_batch_fn(x):\n",
" x = x['title']\n",
" a,b = tf.py_function(title_batch,inp=[x],Tout=(tf.float32,tf.float32))\n",
" return a,b"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"> **Забележка**: Разграничаването между Pythonic и Tensorflow функции за трансформация може да изглежда твърде сложно, и може би се питате защо не трансформираме набора от данни, използвайки стандартни Python функции, преди да го подадем на `fit`. Въпреки че това определено е възможно, използването на `Dataset.map` има огромно предимство, защото тръбопроводът за трансформация на данни се изпълнява чрез изчислителния граф на Tensorflow, който се възползва от изчисленията на GPU и минимизира нуждата от прехвърляне на данни между CPU/GPU.\n",
"\n",
"Сега можем да изградим нашата генераторна мрежа и да започнем обучението. Тя може да бъде базирана на всяка рекурентна клетка, която обсъдихме в предишния модул (обикновена, LSTM или GRU). В нашия пример ще използваме LSTM.\n",
"\n",
"Тъй като мрежата приема символи като вход, а размерът на речника е сравнително малък, нямаме нужда от embedding слой входът, кодиран като one-hot, може директно да бъде подаден в LSTM клетката. Изходният слой ще бъде `Dense` класификатор, който ще преобразува изхода на LSTM в числа, кодирани като one-hot.\n",
"\n",
"Освен това, тъй като работим с последователности с променлива дължина, можем да използваме слой `Masking`, за да създадем маска, която ще игнорира допълнителната част на низа. Това не е строго необходимо, защото не сме особено заинтересовани от всичко, което е след `<eos>` токена, но ще го използваме, за да натрупаме опит с този тип слой. `input_shape` ще бъде `(None, vocab_size)`, където `None` указва последователност с променлива дължина, а изходната форма също е `(None, vocab_size)`, както можете да видите от `summary`:\n"
]
},
{
"cell_type": "code",
"execution_count": 7,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Model: \"sequential\"\n",
"_________________________________________________________________\n",
"Layer (type) Output Shape Param # \n",
"=================================================================\n",
"masking (Masking) (None, None, 84) 0 \n",
"_________________________________________________________________\n",
"lstm (LSTM) (None, None, 128) 109056 \n",
"_________________________________________________________________\n",
"dense (Dense) (None, None, 84) 10836 \n",
"=================================================================\n",
"Total params: 119,892\n",
"Trainable params: 119,892\n",
"Non-trainable params: 0\n",
"_________________________________________________________________\n",
"15000/15000 [==============================] - 229s 15ms/step - loss: 1.5385\n"
]
},
{
"data": {
"text/plain": [
"<tensorflow.python.keras.callbacks.History at 0x7fa40c1245e0>"
]
},
"execution_count": 7,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"model = keras.models.Sequential([\n",
" keras.layers.Masking(input_shape=(None,vocab_size)),\n",
" keras.layers.LSTM(128,return_sequences=True),\n",
" keras.layers.Dense(vocab_size,activation='softmax')\n",
"])\n",
"\n",
"model.summary()\n",
"model.compile(loss='categorical_crossentropy')\n",
"\n",
"model.fit(ds_train.batch(8).map(title_batch_fn))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Генериране на изход\n",
"\n",
"Сега, след като сме обучили модела, искаме да го използваме за генериране на изход. На първо място, ни е необходим начин за декодиране на текст, представен чрез последователност от номера на токени. За тази цел можем да използваме функцията `tokenizer.sequences_to_texts`; обаче тя не работи добре с токенизация на ниво символи. Затова ще вземем речник на токените от токенизатора (наречен `word_index`), ще изградим обратна карта и ще напишем собствена функция за декодиране:\n"
]
},
{
"cell_type": "code",
"execution_count": 10,
"metadata": {},
"outputs": [],
"source": [
"reverse_map = {val:key for key, val in tokenizer.word_index.items()}\n",
"\n",
"def decode(x):\n",
" return ''.join([reverse_map[t] for t in x])"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Сега, нека започнем с генерирането. Ще започнем с някакъв низ `start`, ще го кодираме в последователност `inp`, и след това на всяка стъпка ще извикваме нашата мрежа, за да предскажем следващия символ.\n",
"\n",
"Изходът на мрежата `out` е вектор с `vocab_size` елементи, представляващи вероятностите за всеки токен, и можем да намерим най-вероятния номер на токен, използвайки `argmax`. След това добавяме този символ към генерирания списък с токени и продължаваме с генерирането. Този процес на генериране на един символ се повтаря `size` пъти, за да се генерира необходимият брой символи, и приключваме по-рано, когато се срещне `eos_token`.\n"
]
},
{
"cell_type": "code",
"execution_count": 12,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"'Today #39;s lead to strike for the strike for the strike for the strike (AFP)'"
]
},
"execution_count": 12,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"def generate(model,size=100,start='Today '):\n",
" inp = tokenizer.texts_to_sequences([start])[0]\n",
" chars = inp\n",
" for i in range(size):\n",
" out = model(tf.expand_dims(tf.one_hot(inp,vocab_size),0))[0][-1]\n",
" nc = tf.argmax(out)\n",
" if nc==eos_token:\n",
" break\n",
" chars.append(nc.numpy())\n",
" inp = inp+[nc]\n",
" return decode(chars)\n",
" \n",
"generate(model)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Извличане на примерен изход по време на обучение\n",
"\n",
"Тъй като нямаме полезни метрики като *точност*, единственият начин да видим дали нашият модел се подобрява, е чрез **извличане на примерен** генериран низ по време на обучението. За да направим това, ще използваме **callbacks**, т.е. функции, които можем да предадем на функцията `fit` и които ще бъдат извиквани периодично по време на обучението.\n"
]
},
{
"cell_type": "code",
"execution_count": 13,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Epoch 1/3\n",
"15000/15000 [==============================] - 226s 15ms/step - loss: 1.2703\n",
"Today #39;s a lead in the company for the strike\n",
"Epoch 2/3\n",
"15000/15000 [==============================] - 227s 15ms/step - loss: 1.2057\n",
"Today #39;s the Market Service on Security Start (AP)\n",
"Epoch 3/3\n",
"15000/15000 [==============================] - 226s 15ms/step - loss: 1.1752\n",
"Today #39;s a line on the strike to start for the start\n"
]
},
{
"data": {
"text/plain": [
"<tensorflow.python.keras.callbacks.History at 0x7fa40c74e3d0>"
]
},
"execution_count": 13,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"sampling_callback = keras.callbacks.LambdaCallback(\n",
" on_epoch_end = lambda batch, logs: print(generate(model))\n",
")\n",
"\n",
"model.fit(ds_train.batch(8).map(title_batch_fn),callbacks=[sampling_callback],epochs=3)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Този пример вече генерира доста добър текст, но може да бъде подобрен по няколко начина:\n",
"\n",
"* **Повече текст**. Използвахме само заглавия за нашата задача, но може да опитате с пълен текст. Имайте предвид, че RNNs не се справят добре с обработката на дълги последователности, затова има смисъл или да ги разделите на по-кратки изречения, или винаги да тренирате с фиксирана дължина на последователността с предварително зададена стойност `num_chars` (например, 256). Можете да опитате да промените горния пример в такава архитектура, използвайки [официалния урок на Keras](https://keras.io/examples/generative/lstm_character_level_text_generation/) като вдъхновение.\n",
"\n",
"* **Многослойна LSTM**. Има смисъл да опитате с 2 или 3 слоя LSTM клетки. Както споменахме в предишния модул, всеки слой на LSTM извлича определени модели от текста, и в случай на генератор на ниво символи можем да очакваме, че по-ниският слой на LSTM ще бъде отговорен за извличането на срички, а по-високите слоеве - за думи и комбинации от думи. Това може лесно да се реализира чрез подаване на параметър за броя на слоевете към конструктора на LSTM.\n",
"\n",
"* Може също да искате да експериментирате с **GRU единици** и да видите кои се представят по-добре, както и с **различни размери на скритите слоеве**. Твърде голям скрит слой може да доведе до прекомерно напасване (например, мрежата ще научи точния текст), а по-малък размер може да не произведе добри резултати.\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Генериране на мек текст и температура\n",
"\n",
"В предишното определение на `generate`, винаги избирахме символа с най-висока вероятност като следващ символ в генерирания текст. Това водеше до факта, че текстът често \"циклираше\" между едни и същи последователности от символи отново и отново, както в този пример:\n",
"```\n",
"today of the second the company and a second the company ...\n",
"```\n",
"\n",
"Но ако погледнем разпределението на вероятностите за следващия символ, може да се окаже, че разликата между няколко от най-високите вероятности не е голяма, например един символ може да има вероятност 0.2, а друг - 0.19 и т.н. Например, когато търсим следващия символ в последователността '*play*', следващият символ може еднакво добре да бъде интервал или **e** (както в думата *player*).\n",
"\n",
"Това ни води до извода, че не винаги е \"справедливо\" да избираме символа с по-висока вероятност, защото изборът на втория по вероятност също може да доведе до смислен текст. По-разумно е да **вземем проба** от разпределението на вероятностите, дадено от изхода на мрежата.\n",
"\n",
"Това вземане на проба може да се извърши с помощта на функцията `np.multinomial`, която реализира така нареченото **мултиномиално разпределение**. Функция, която реализира това **меко** генериране на текст, е дефинирана по-долу:\n"
]
},
{
"cell_type": "code",
"execution_count": 33,
"metadata": {
"scrolled": true
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"\n",
"--- Temperature = 0.3\n",
"Today #39;s strike #39; to start at the store return\n",
"On Sunday PO to Be Data Profit Up (Reuters)\n",
"Moscow, SP wins straight to the Microsoft #39;s control of the space start\n",
"President olding of the blast start for the strike to pay &lt;b&gt;...&lt;/b&gt;\n",
"Little red riding hood ficed to the spam countered in European &lt;b&gt;...&lt;/b&gt;\n",
"\n",
"--- Temperature = 0.8\n",
"Today countie strikes ryder missile faces food market blut\n",
"On Sunday collores lose-toppy of sale of Bullment in &lt;b&gt;...&lt;/b&gt;\n",
"Moscow, IBM Diffeiting in Afghan Software Hotels (Reuters)\n",
"President Ol Luster for Profit Peaced Raised (AP)\n",
"Little red riding hood dace on depart talks #39; bank up\n",
"\n",
"--- Temperature = 1.0\n",
"Today wits House buiting debate fixes #39; supervice stake again\n",
"On Sunday arling digital poaching In for level\n",
"Moscow, DS Up 7, Top Proble Protest Caprey Mamarian Strike\n",
"President teps help of roubler stepted lessabul-Dhalitics (AFP)\n",
"Little red riding hood signs on cash in Carter-youb\n",
"\n",
"--- Temperature = 1.3\n",
"Today wits flawer ro, pSIA figat's co DroftwavesIs Talo up\n",
"On Sunday hround elitwing wint EU Powerburlinetien\n",
"Moscow, Bazz #39;s sentries olymen winnelds' next for Olympite Huc?\n",
"President lost securitys from power Elections in Smiltrials\n",
"Little red riding hood vides profit, exponituity, profitmainalist-at said listers\n",
"\n",
"--- Temperature = 1.8\n",
"Today #39;It: He deat: N.KA Asside\n",
"On Sunday i arry Par aldeup patient Wo stele1\n"
]
},
{
"ename": "KeyError",
"evalue": "0",
"output_type": "error",
"traceback": [
"\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
"\u001b[0;31mKeyError\u001b[0m Traceback (most recent call last)",
"\u001b[0;32m<ipython-input-33-db32367a0feb>\u001b[0m in \u001b[0;36m<module>\u001b[0;34m\u001b[0m\n\u001b[1;32m 18\u001b[0m \u001b[0mprint\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34mf\"\\n--- Temperature = {i}\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 19\u001b[0m \u001b[0;32mfor\u001b[0m \u001b[0mj\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mrange\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;36m5\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 20\u001b[0;31m \u001b[0mprint\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mgenerate_soft\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mmodel\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0msize\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;36m300\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0mstart\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mwords\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0mj\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0mtemperature\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mi\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m",
"\u001b[0;32m<ipython-input-33-db32367a0feb>\u001b[0m in \u001b[0;36mgenerate_soft\u001b[0;34m(model, size, start, temperature)\u001b[0m\n\u001b[1;32m 11\u001b[0m \u001b[0mchars\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mappend\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mnc\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 12\u001b[0m \u001b[0minp\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0minp\u001b[0m\u001b[0;34m+\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0mnc\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 13\u001b[0;31m \u001b[0;32mreturn\u001b[0m \u001b[0mdecode\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mchars\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 14\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 15\u001b[0m \u001b[0mwords\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;34m[\u001b[0m\u001b[0;34m'Today '\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m'On Sunday '\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m'Moscow, '\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m'President '\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m'Little red riding hood '\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
"\u001b[0;32m<ipython-input-10-3f5fa6130b1d>\u001b[0m in \u001b[0;36mdecode\u001b[0;34m(x)\u001b[0m\n\u001b[1;32m 2\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 3\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0mdecode\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mx\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 4\u001b[0;31m \u001b[0;32mreturn\u001b[0m \u001b[0;34m''\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mjoin\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0mreverse_map\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0mt\u001b[0m\u001b[0;34m]\u001b[0m \u001b[0;32mfor\u001b[0m \u001b[0mt\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mx\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m",
"\u001b[0;32m<ipython-input-10-3f5fa6130b1d>\u001b[0m in \u001b[0;36m<listcomp>\u001b[0;34m(.0)\u001b[0m\n\u001b[1;32m 2\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 3\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0mdecode\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mx\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 4\u001b[0;31m \u001b[0;32mreturn\u001b[0m \u001b[0;34m''\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mjoin\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0mreverse_map\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0mt\u001b[0m\u001b[0;34m]\u001b[0m \u001b[0;32mfor\u001b[0m \u001b[0mt\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mx\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m",
"\u001b[0;31mKeyError\u001b[0m: 0"
]
}
],
"source": [
"def generate_soft(model,size=100,start='Today ',temperature=1.0):\n",
" inp = tokenizer.texts_to_sequences([start])[0]\n",
" chars = inp\n",
" for i in range(size):\n",
" out = model(tf.expand_dims(tf.one_hot(inp,vocab_size),0))[0][-1]\n",
" probs = tf.exp(tf.math.log(out)/temperature).numpy().astype(np.float64)\n",
" probs = probs/np.sum(probs)\n",
" nc = np.argmax(np.random.multinomial(1,probs,1))\n",
" if nc==eos_token:\n",
" break\n",
" chars.append(nc)\n",
" inp = inp+[nc]\n",
" return decode(chars)\n",
"\n",
"words = ['Today ','On Sunday ','Moscow, ','President ','Little red riding hood ']\n",
" \n",
"for i in [0.3,0.8,1.0,1.3,1.8]:\n",
" print(f\"\\n--- Temperature = {i}\")\n",
" for j in range(5):\n",
" print(generate_soft(model,size=300,start=words[j],temperature=i))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Въведохме още един параметър, наречен **температура**, който се използва, за да покаже колко строго трябва да се придържаме към най-високата вероятност. Ако температурата е 1.0, правим честно мултиномиално извадково вземане, а когато температурата достигне до безкрайност - всички вероятности стават равни и случайно избираме следващия символ. В примера по-долу можем да наблюдаваме, че текстът става безсмислен, когато увеличим температурата твърде много, и прилича на \"циклично\" твърдо генериран текст, когато се доближи до 0.\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"\n---\n\n**Отказ от отговорност**: \nТози документ е преведен с помощта на AI услуга за превод [Co-op Translator](https://github.com/Azure/co-op-translator). Въпреки че се стремим към точност, моля, имайте предвид, че автоматизираните преводи може да съдържат грешки или неточности. Оригиналният документ на неговия роден език трябва да се счита за авторитетен източник. За критична информация се препоръчва професионален човешки превод. Ние не носим отговорност за каквито и да е недоразумения или погрешни интерпретации, произтичащи от използването на този превод.\n"
]
}
],
"metadata": {
"interpreter": {
"hash": "16af2a8bbb083ea23e5e41c7f5787656b2ce26968575d8763f2c4b17f9cd711f"
},
"kernelspec": {
"display_name": "Python 3.8.12 ('py38')",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.8.12"
},
"coopTranslator": {
"original_hash": "9fbb7d5fda708537649f71f5f646fcde",
"translation_date": "2025-08-30T00:35:18+00:00",
"source_file": "lessons/5-NLP/17-GenerativeNetworks/GenerativeTF.ipynb",
"language_code": "bg"
}
},
"nbformat": 4,
"nbformat_minor": 4
}

View File

@ -0,0 +1,353 @@
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Механизми на вниманието и трансформери\n",
"\n",
"Един от основните недостатъци на рекурентните мрежи е, че всички думи в последователността имат еднакво влияние върху резултата. Това води до неефективност при стандартните LSTM encoder-decoder модели за задачи от тип последователност към последователност, като разпознаване на именувани обекти и машинен превод. В действителност, определени думи в входната последователност често имат по-голямо влияние върху изходните последователности от други.\n",
"\n",
"Да разгледаме модел от тип последователност към последователност, като машинен превод. Той се реализира чрез две рекурентни мрежи, където едната мрежа (**encoder**) компресира входната последователност в скрито състояние, а другата (**decoder**) разгъва това скрито състояние в преведен резултат. Проблемът с този подход е, че крайната състояние на мрежата трудно запомня началото на изречението, което води до ниско качество на модела при дълги изречения.\n",
"\n",
"**Механизмите на вниманието** предоставят начин за претегляне на контекстуалното влияние на всеки входен вектор върху всяка изходна прогноза на RNN. Това се реализира чрез създаване на преки връзки между междинните състояния на входната RNN и изходната RNN. По този начин, при генериране на изходен символ $y_t$, ще вземем предвид всички входни скрити състояния $h_i$, с различни теглови коефициенти $\\alpha_{t,i}$.\n",
"\n",
"![Изображение, показващо модел encoder/decoder с добавен слой за внимание](../../../../../translated_images/encoder-decoder-attention.7a726296894fb567aa2898c94b17b3289087f6705c11907df8301df9e5eeb3de.bg.png)\n",
"*Моделът encoder-decoder с механизъм за добавено внимание в [Bahdanau et al., 2015](https://arxiv.org/pdf/1409.0473.pdf), цитиран от [този блог пост](https://lilianweng.github.io/lil-log/2018/06/24/attention-attention.html)*\n",
"\n",
"Матрицата на вниманието $\\{\\alpha_{i,j}\\}$ представлява степента, до която определени входни думи участват в генерирането на дадена дума в изходната последователност. По-долу е даден пример за такава матрица:\n",
"\n",
"![Изображение, показващо пример за подравняване, намерено от RNNsearch-50, взето от Bahdanau - arviz.org](../../../../../translated_images/bahdanau-fig3.09ba2d37f202a6af11de6c82d2d197830ba5f4528d9ea430eb65fd3a75065973.bg.png)\n",
"\n",
"*Фигура, взета от [Bahdanau et al., 2015](https://arxiv.org/pdf/1409.0473.pdf) (Фиг.3)*\n",
"\n",
"Механизмите на вниманието са отговорни за голяма част от настоящите или почти настоящите върхови постижения в обработката на естествен език. Добавянето на внимание обаче значително увеличава броя на параметрите на модела, което води до проблеми със скалирането при RNN. Основно ограничение при скалирането на RNN е, че рекурентната природа на моделите затруднява групирането и паралелизирането на обучението. В RNN всеки елемент от последователността трябва да бъде обработен в последователен ред, което означава, че не може лесно да бъде паралелизиран.\n",
"\n",
"Приемането на механизми на вниманието в комбинация с това ограничение доведе до създаването на съвременните трансформерни модели, които познаваме и използваме днес, като BERT и OpenGPT3.\n",
"\n",
"## Трансформерни модели\n",
"\n",
"Вместо да предават контекста на всяка предишна прогноза в следващата стъпка на оценка, **трансформерните модели** използват **позиционни кодировки** и внимание, за да уловят контекста на даден вход в предоставен прозорец от текст. Изображението по-долу показва как позиционните кодировки с внимание могат да уловят контекста в даден прозорец.\n",
"\n",
"![Анимиран GIF, показващ как се извършват оценките в трансформерните модели.](../../../../../lessons/5-NLP/18-Transformers/images/transformer-animated-explanation.gif)\n",
"\n",
"Тъй като всяка входна позиция се съпоставя независимо с всяка изходна позиция, трансформерите могат да се паралелизират по-добре от RNN, което позволява създаването на много по-големи и изразителни езикови модели. Всяка глава на вниманието може да се използва за изучаване на различни взаимоотношения между думите, което подобрява задачите за обработка на естествен език.\n",
"\n",
"**BERT** (Bidirectional Encoder Representations from Transformers) е много голяма многослойна трансформерна мрежа с 12 слоя за *BERT-base* и 24 за *BERT-large*. Моделът първо се предварително обучава върху голям корпус от текстови данни (Wikipedia + книги) с помощта на неконтролирано обучение (предсказване на маскирани думи в изречение). По време на предварителното обучение моделът усвоява значително ниво на езиково разбиране, което след това може да бъде използвано с други набори от данни чрез фина настройка. Този процес се нарича **трансферно обучение**.\n",
"\n",
"![изображение от http://jalammar.github.io/illustrated-bert/](../../../../../translated_images/jalammarBERT-language-modeling-masked-lm.34f113ea5fec4362e39ee4381aab7cad06b5465a0b5f053a0f2aa05fbe14e746.bg.png)\n",
"\n",
"Съществуват много вариации на трансформерните архитектури, включително BERT, DistilBERT, BigBird, OpenGPT3 и други, които могат да бъдат фино настроени. Пакетът [HuggingFace](https://github.com/huggingface/) предоставя хранилище за обучение на много от тези архитектури с PyTorch.\n",
"\n",
"## Използване на BERT за текстова класификация\n",
"\n",
"Нека видим как можем да използваме предварително обучен модел BERT за решаване на нашата традиционна задача: класификация на последователности. Ще класифицираме оригиналния ни набор от данни AG News.\n",
"\n",
"Първо, нека заредим библиотеката HuggingFace и нашия набор от данни:\n"
]
},
{
"cell_type": "code",
"execution_count": 10,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Loading dataset...\n",
"Building vocab...\n"
]
}
],
"source": [
"import torch\n",
"import torchtext\n",
"from torchnlp import *\n",
"import transformers\n",
"train_dataset, test_dataset, classes, vocab = load_dataset()\n",
"vocab_len = len(vocab)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Тъй като ще използваме предварително обучен BERT модел, ще трябва да използваме специфичен токенизатор. Първо, ще заредим токенизатор, свързан с предварително обучен BERT модел.\n",
"\n",
"Библиотеката HuggingFace съдържа хранилище с предварително обучени модели, които можете да използвате, като просто посочите техните имена като аргументи към функциите `from_pretrained`. Всички необходими бинарни файлове за модела ще бъдат автоматично изтеглени.\n",
"\n",
"Въпреки това, понякога може да се наложи да заредите свои собствени модели. В този случай можете да посочите директорията, която съдържа всички съответни файлове, включително параметрите за токенизатора, файла `config.json` с параметрите на модела, бинарните тегла и др.\n"
]
},
{
"cell_type": "code",
"execution_count": 11,
"metadata": {},
"outputs": [],
"source": [
"# To load the model from Internet repository using model name. \n",
"# Use this if you are running from your own copy of the notebooks\n",
"bert_model = 'bert-base-uncased' \n",
"\n",
"# To load the model from the directory on disk. Use this for Microsoft Learn module, because we have\n",
"# prepared all required files for you.\n",
"bert_model = './bert'\n",
"\n",
"tokenizer = transformers.BertTokenizer.from_pretrained(bert_model)\n",
"\n",
"MAX_SEQ_LEN = 128\n",
"PAD_INDEX = tokenizer.convert_tokens_to_ids(tokenizer.pad_token)\n",
"UNK_INDEX = tokenizer.convert_tokens_to_ids(tokenizer.unk_token)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Обектът `tokenizer` съдържа функцията `encode`, която може да се използва директно за кодиране на текст:\n"
]
},
{
"cell_type": "code",
"execution_count": 15,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"[101, 1052, 22123, 2953, 2818, 2003, 1037, 2307, 7705, 2005, 17953, 2361, 102]"
]
},
"execution_count": 15,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"tokenizer.encode('PyTorch is a great framework for NLP')"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Тогава нека създадем итератори, които ще използваме по време на обучението за достъп до данните. Тъй като BERT използва своя собствена функция за кодиране, ще трябва да дефинираме функция за запълване, подобна на `padify`, която сме дефинирали преди:\n"
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {},
"outputs": [],
"source": [
"def pad_bert(b):\n",
" # b is the list of tuples of length batch_size\n",
" # - first element of a tuple = label, \n",
" # - second = feature (text sequence)\n",
" # build vectorized sequence\n",
" v = [tokenizer.encode(x[1]) for x in b]\n",
" # compute max length of a sequence in this minibatch\n",
" l = max(map(len,v))\n",
" return ( # tuple of two tensors - labels and features\n",
" torch.LongTensor([t[0] for t in b]),\n",
" torch.stack([torch.nn.functional.pad(torch.tensor(t),(0,l-len(t)),mode='constant',value=0) for t in v])\n",
" )\n",
"\n",
"train_loader = torch.utils.data.DataLoader(train_dataset, batch_size=8, collate_fn=pad_bert, shuffle=True)\n",
"test_loader = torch.utils.data.DataLoader(test_dataset, batch_size=8, collate_fn=pad_bert)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"В нашия случай ще използваме предварително обучен BERT модел, наречен `bert-base-uncased`. Нека заредим модела, използвайки пакета `BertForSequenceClassfication`. Това гарантира, че нашият модел вече има необходимата архитектура за класификация, включително финалния класификатор. Ще видите предупредително съобщение, което посочва, че теглата на финалния класификатор не са инициализирани и моделът ще изисква предварително обучение - това е напълно нормално, защото точно това ще направим!\n"
]
},
{
"cell_type": "code",
"execution_count": 9,
"metadata": {},
"outputs": [
{
"name": "stderr",
"output_type": "stream",
"text": [
"Some weights of the model checkpoint at ./bert were not used when initializing BertForSequenceClassification: ['cls.predictions.bias', 'cls.predictions.transform.dense.weight', 'cls.predictions.transform.dense.bias', 'cls.predictions.decoder.weight', 'cls.seq_relationship.weight', 'cls.seq_relationship.bias', 'cls.predictions.transform.LayerNorm.weight', 'cls.predictions.transform.LayerNorm.bias']\n",
"- This IS expected if you are initializing BertForSequenceClassification from the checkpoint of a model trained on another task or with another architecture (e.g. initializing a BertForSequenceClassification model from a BertForPreTraining model).\n",
"- This IS NOT expected if you are initializing BertForSequenceClassification from the checkpoint of a model that you expect to be exactly identical (initializing a BertForSequenceClassification model from a BertForSequenceClassification model).\n",
"Some weights of BertForSequenceClassification were not initialized from the model checkpoint at ./bert and are newly initialized: ['classifier.weight', 'classifier.bias']\n",
"You should probably TRAIN this model on a down-stream task to be able to use it for predictions and inference.\n"
]
}
],
"source": [
"model = transformers.BertForSequenceClassification.from_pretrained(bert_model,num_labels=4).to(device)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Сега сме готови да започнем обучението! Тъй като BERT вече е предварително обучен, искаме да започнем с доста малка скорост на обучение, за да не разрушим началните тегла.\n",
"\n",
"Цялата тежка работа се извършва от модела `BertForSequenceClassification`. Когато извикаме модела върху тренировъчните данни, той връща както загубата, така и изхода на мрежата за входния минибатч. Използваме загубата за оптимизация на параметрите (`loss.backward()` извършва обратното преминаване), а `out` за изчисляване на точността на обучението, като сравняваме получените етикети `labs` (изчислени с помощта на `argmax`) с очакваните `labels`.\n",
"\n",
"За да контролираме процеса, натрупваме загубата и точността през няколко итерации и ги отпечатваме на всеки `report_freq` цикли на обучение.\n",
"\n",
"Това обучение вероятно ще отнеме доста време, затова ограничаваме броя на итерациите.\n"
]
},
{
"cell_type": "code",
"execution_count": 6,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Loss = 1.1254194641113282, Accuracy = 0.585\n",
"Loss = 0.6194715118408203, Accuracy = 0.83\n",
"Loss = 0.46665248870849607, Accuracy = 0.8475\n",
"Loss = 0.4309701919555664, Accuracy = 0.8575\n",
"Loss = 0.35427074432373046, Accuracy = 0.8825\n",
"Loss = 0.3306886291503906, Accuracy = 0.8975\n",
"Loss = 0.30340143203735354, Accuracy = 0.8975\n",
"Loss = 0.26139299392700194, Accuracy = 0.915\n",
"Loss = 0.26708646774291994, Accuracy = 0.9225\n",
"Loss = 0.3667240524291992, Accuracy = 0.8675\n"
]
}
],
"source": [
"optimizer = torch.optim.Adam(model.parameters(), lr=2e-5)\n",
"\n",
"report_freq = 50\n",
"iterations = 500 # make this larger to train for longer time!\n",
"\n",
"model.train()\n",
"\n",
"i,c = 0,0\n",
"acc_loss = 0\n",
"acc_acc = 0\n",
"\n",
"for labels,texts in train_loader:\n",
" labels = labels.to(device)-1 # get labels in the range 0-3 \n",
" texts = texts.to(device)\n",
" loss, out = model(texts, labels=labels)[:2]\n",
" labs = out.argmax(dim=1)\n",
" acc = torch.mean((labs==labels).type(torch.float32))\n",
" optimizer.zero_grad()\n",
" loss.backward()\n",
" optimizer.step()\n",
" acc_loss += loss\n",
" acc_acc += acc\n",
" i+=1\n",
" c+=1\n",
" if i%report_freq==0:\n",
" print(f\"Loss = {acc_loss.item()/c}, Accuracy = {acc_acc.item()/c}\")\n",
" c = 0\n",
" acc_loss = 0\n",
" acc_acc = 0\n",
" iterations-=1\n",
" if not iterations:\n",
" break"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Можете да видите (особено ако увеличите броя на итерациите и изчакате достатъчно дълго), че класификацията с BERT ни дава доста добра точност! Това е така, защото BERT вече разбира доста добре структурата на езика и ние само трябва да дообучим крайния класификатор. Въпреки това, тъй като BERT е голям модел, целият процес на обучение отнема много време и изисква сериозна изчислителна мощност! (GPU, и за предпочитане повече от един).\n",
"\n",
"> **Note:** В нашия пример използвахме един от най-малките предварително обучени модели на BERT. Съществуват по-големи модели, които вероятно ще дадат по-добри резултати.\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Оценка на производителността на модела\n",
"\n",
"Сега можем да оценим производителността на нашия модел върху тестовия набор от данни. Цикълът за оценка е доста подобен на цикъла за обучение, но не трябва да забравяме да превключим модела в режим на оценка, като извикаме `model.eval()`.\n"
]
},
{
"cell_type": "code",
"execution_count": 10,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Final accuracy: 0.9047029702970297\n"
]
}
],
"source": [
"model.eval()\n",
"iterations = 100\n",
"acc = 0\n",
"i = 0\n",
"for labels,texts in test_loader:\n",
" labels = labels.to(device)-1 \n",
" texts = texts.to(device)\n",
" _, out = model(texts, labels=labels)[:2]\n",
" labs = out.argmax(dim=1)\n",
" acc += torch.mean((labs==labels).type(torch.float32))\n",
" i+=1\n",
" if i>iterations: break\n",
" \n",
"print(f\"Final accuracy: {acc.item()/i}\")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Основни моменти\n",
"\n",
"В този модул видяхме колко лесно е да вземем предварително обучен езиков модел от библиотеката **transformers** и да го адаптираме към нашата задача за класификация на текст. По същия начин, моделите BERT могат да се използват за извличане на обекти, отговаряне на въпроси и други задачи, свързани с обработка на естествен език.\n",
"\n",
"Моделите на трансформъри представляват най-съвременните технологии в NLP и в повечето случаи трябва да бъдат първото решение, с което започвате експерименти, когато внедрявате персонализирани NLP решения. Въпреки това, разбирането на основните принципи на рекурентните невронни мрежи, обсъдени в този модул, е изключително важно, ако искате да изграждате усъвършенствани невронни модели.\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"\n---\n\n**Отказ от отговорност**: \nТози документ е преведен с помощта на AI услуга за превод [Co-op Translator](https://github.com/Azure/co-op-translator). Въпреки че се стремим към точност, моля, имайте предвид, че автоматизираните преводи може да съдържат грешки или неточности. Оригиналният документ на неговия роден език трябва да се счита за авторитетен източник. За критична информация се препоръчва професионален човешки превод. Ние не носим отговорност за каквито и да е недоразумения или погрешни интерпретации, произтичащи от използването на този превод.\n"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "py37_pytorch",
"language": "python",
"name": "conda-env-py37_pytorch-py"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.7.7"
},
"coopTranslator": {
"original_hash": "753865967678a92dbce7d7efbd36d980",
"translation_date": "2025-08-30T00:45:25+00:00",
"source_file": "lessons/5-NLP/18-Transformers/TransformersPyTorch.ipynb",
"language_code": "bg"
}
},
"nbformat": 4,
"nbformat_minor": 4
}

View File

@ -0,0 +1,819 @@
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Механизми на внимание и трансформери\n",
"\n",
"Един от основните недостатъци на рекурентните мрежи е, че всички думи в последователността имат еднакво влияние върху резултата. Това води до подоптимална производителност при стандартните LSTM модели за енкодер-декодер за задачи от тип последователност към последователност, като разпознаване на именувани обекти и машинен превод. В действителност, определени думи в входната последователност често имат по-голямо влияние върху изходните последователности от други.\n",
"\n",
"Да разгледаме модел от тип последователност към последователност, като машинен превод. Той се реализира чрез две рекурентни мрежи, където едната мрежа (**енкодер**) компресира входната последователност в скрито състояние, а другата, **декодер**, разгъва това скрито състояние в преведен резултат. Проблемът с този подход е, че крайното състояние на мрежата трудно запомня началото на изречението, което води до ниско качество на модела при дълги изречения.\n",
"\n",
"**Механизмите на внимание** предоставят начин за претегляне на контекстуалното влияние на всеки входен вектор върху всяка изходна прогноза на RNN. Това се реализира чрез създаване на преки връзки между междинните състояния на входната RNN и изходната RNN. По този начин, при генериране на изходен символ $y_t$, ще вземем предвид всички входни скрити състояния $h_i$, с различни теглови коефициенти $\\alpha_{t,i}$.\n",
"\n",
"![Изображение, показващо модел енкодер/декодер с добавен слой за внимание](../../../../../translated_images/encoder-decoder-attention.7a726296894fb567aa2898c94b17b3289087f6705c11907df8301df9e5eeb3de.bg.png)\n",
"*Моделът енкодер-декодер с механизъм за добавено внимание в [Bahdanau et al., 2015](https://arxiv.org/pdf/1409.0473.pdf), цитиран от [този блог пост](https://lilianweng.github.io/lil-log/2018/06/24/attention-attention.html)*\n",
"\n",
"Матрицата на внимание $\\{\\alpha_{i,j}\\}$ представлява степента, до която определени входни думи играят роля в генерирането на дадена дума в изходната последователност. По-долу е даден пример за такава матрица:\n",
"\n",
"![Изображение, показващо пример за подравняване, намерено от RNNsearch-50, взето от Bahdanau - arviz.org](../../../../../translated_images/bahdanau-fig3.09ba2d37f202a6af11de6c82d2d197830ba5f4528d9ea430eb65fd3a75065973.bg.png)\n",
"\n",
"*Фигура, взета от [Bahdanau et al., 2015](https://arxiv.org/pdf/1409.0473.pdf) (Фиг.3)*\n",
"\n",
"Механизмите на внимание са отговорни за голяма част от настоящите или почти настоящите върхови постижения в обработката на естествен език. Добавянето на внимание обаче значително увеличава броя на параметрите на модела, което води до проблеми със скалирането при RNN. Основно ограничение при скалирането на RNN е, че рекурентната природа на моделите затруднява групирането и паралелизирането на обучението. В RNN всеки елемент от последователността трябва да бъде обработен в последователен ред, което означава, че не може лесно да бъде паралелизиран.\n",
"\n",
"Приемането на механизми на внимание, комбинирано с това ограничение, доведе до създаването на съвременните трансформер модели, които познаваме и използваме днес, от BERT до OpenGPT3.\n",
"\n",
"## Трансформер модели\n",
"\n",
"Вместо да предават контекста на всяка предишна прогноза в следващата стъпка на оценка, **трансформер моделите** използват **позиционни кодировки** и **внимание**, за да уловят контекста на даден вход в предоставен прозорец от текст. Изображението по-долу показва как позиционните кодировки с внимание могат да уловят контекста в даден прозорец.\n",
"\n",
"![Анимиран GIF, показващ как се извършват оценките в трансформер моделите.](../../../../../lessons/5-NLP/18-Transformers/images/transformer-animated-explanation.gif)\n",
"\n",
"Тъй като всяка входна позиция се съпоставя независимо с всяка изходна позиция, трансформерите могат да се паралелизират по-добре от RNN, което позволява създаването на много по-големи и по-изразителни езикови модели. Всяка глава на внимание може да се използва за изучаване на различни взаимоотношения между думите, което подобрява задачите за обработка на естествен език.\n",
"\n",
"## Създаване на прост трансформер модел\n",
"\n",
"Keras не съдържа вградени слоеве за трансформери, но можем да създадем свои собствени. Както и преди, ще се фокусираме върху текстова класификация на набора от данни AG News, но си струва да се отбележи, че трансформер моделите показват най-добри резултати при по-сложни задачи за обработка на естествен език.\n"
]
},
{
"cell_type": "code",
"execution_count": 1,
"metadata": {},
"outputs": [],
"source": [
"import tensorflow as tf\n",
"from tensorflow import keras\n",
"import tensorflow_datasets as tfds\n",
"import numpy as np\n",
"\n",
"ds_train, ds_test = tfds.load('ag_news_subset').values()\n",
"\n",
"def extract_text(x):\n",
" return x['title']+' '+x['description']\n",
"\n",
"def tupelize(x):\n",
" return (extract_text(x),x['label'])"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Новите слоеве в Keras трябва да наследяват класа `Layer` и да имплементират метода `call`. Нека започнем със слоя **Positional Embedding**. Ще използваме [някакъв код от официалната документация на Keras](https://keras.io/examples/nlp/text_classification_with_transformer/). Ще приемем, че запълваме всички входни последователности до дължина `maxlen`.\n"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {},
"outputs": [],
"source": [
"class TokenAndPositionEmbedding(keras.layers.Layer):\n",
" def __init__(self, maxlen, vocab_size, embed_dim):\n",
" super(TokenAndPositionEmbedding, self).__init__()\n",
" self.token_emb = keras.layers.Embedding(input_dim=vocab_size, output_dim=embed_dim)\n",
" self.pos_emb = keras.layers.Embedding(input_dim=maxlen, output_dim=embed_dim)\n",
" self.maxlen = maxlen\n",
"\n",
" def call(self, x):\n",
" maxlen = self.maxlen\n",
" positions = tf.range(start=0, limit=maxlen, delta=1)\n",
" positions = self.pos_emb(positions)\n",
" x = self.token_emb(x)\n",
" return x+positions"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Този слой се състои от два слоя `Embedding`: за вграждане на токени (по начина, който обсъдихме по-рано) и за вграждане на позициите на токените. Позициите на токените се създават като последователност от естествени числа от 0 до `maxlen`, използвайки `tf.range`, и след това се подават през слоя за вграждане. Двата получени векторa за вграждане се събират, като се получава позиционно вградено представяне на входа с форма `maxlen`$\\times$`embed_dim`.\n",
"\n",
"Сега, нека имплементираме блока на трансформъра. Той ще използва изхода от предварително дефинирания слой за вграждане:\n"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {},
"outputs": [],
"source": [
"class TransformerBlock(keras.layers.Layer):\n",
" def __init__(self, embed_dim, num_heads, ff_dim, rate=0.1):\n",
" super(TransformerBlock, self).__init__()\n",
" self.att = keras.layers.MultiHeadAttention(num_heads=num_heads, key_dim=embed_dim, name='attn')\n",
" self.ffn = keras.Sequential(\n",
" [keras.layers.Dense(ff_dim, activation=\"relu\"), keras.layers.Dense(embed_dim),]\n",
" )\n",
" self.layernorm1 = keras.layers.LayerNormalization(epsilon=1e-6)\n",
" self.layernorm2 = keras.layers.LayerNormalization(epsilon=1e-6)\n",
" self.dropout1 = keras.layers.Dropout(rate)\n",
" self.dropout2 = keras.layers.Dropout(rate)\n",
"\n",
" def call(self, inputs, training):\n",
" attn_output = self.att(inputs, inputs)\n",
" attn_output = self.dropout1(attn_output, training=training)\n",
" out1 = self.layernorm1(inputs + attn_output)\n",
" ffn_output = self.ffn(out1)\n",
" ffn_output = self.dropout2(ffn_output, training=training)\n",
" return self.layernorm2(out1 + ffn_output)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Сега сме готови да дефинираме пълния модел на трансформър:\n"
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Model: \"sequential_1\"\n",
"_________________________________________________________________\n",
"Layer (type) Output Shape Param # \n",
"=================================================================\n",
"text_vectorization (TextVect (None, 256) 0 \n",
"_________________________________________________________________\n",
"token_and_position_embedding (None, 256, 32) 648192 \n",
"_________________________________________________________________\n",
"transformer_block (Transform (None, 256, 32) 10656 \n",
"_________________________________________________________________\n",
"global_average_pooling1d (Gl (None, 32) 0 \n",
"_________________________________________________________________\n",
"dropout_2 (Dropout) (None, 32) 0 \n",
"_________________________________________________________________\n",
"dense_2 (Dense) (None, 20) 660 \n",
"_________________________________________________________________\n",
"dropout_3 (Dropout) (None, 20) 0 \n",
"_________________________________________________________________\n",
"dense_3 (Dense) (None, 4) 84 \n",
"=================================================================\n",
"Total params: 659,592\n",
"Trainable params: 659,592\n",
"Non-trainable params: 0\n",
"_________________________________________________________________\n"
]
}
],
"source": [
"embed_dim = 32 # Embedding size for each token\n",
"num_heads = 2 # Number of attention heads\n",
"ff_dim = 32 # Hidden layer size in feed forward network inside transformer\n",
"maxlen = 256\n",
"vocab_size = 20000\n",
"\n",
"model = keras.models.Sequential([\n",
" keras.layers.experimental.preprocessing.TextVectorization(max_tokens=vocab_size,output_sequence_length=maxlen, input_shape=(1,)),\n",
" TokenAndPositionEmbedding(maxlen, vocab_size, embed_dim),\n",
" TransformerBlock(embed_dim, num_heads, ff_dim),\n",
" keras.layers.GlobalAveragePooling1D(),\n",
" keras.layers.Dropout(0.1),\n",
" keras.layers.Dense(20, activation=\"relu\"),\n",
" keras.layers.Dropout(0.1),\n",
" keras.layers.Dense(4, activation=\"softmax\")\n",
"])\n",
"\n",
"model.summary()"
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Training tokenizer\n",
"938/938 [==============================] - 45s 39ms/step - loss: 0.4978 - acc: 0.8068 - val_loss: 0.2808 - val_acc: 0.9124\n"
]
},
{
"data": {
"text/plain": [
"<tensorflow.python.keras.callbacks.History at 0x7f9c2427a0d0>"
]
},
"execution_count": 5,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"print('Training tokenizer')\n",
"model.layers[0].adapt(ds_train.map(extract_text))\n",
"model.compile(loss='sparse_categorical_crossentropy',metrics=['acc'], optimizer='adam')\n",
"model.fit(ds_train.map(tupelize).batch(128),validation_data=ds_test.map(tupelize).batch(128))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## BERT Transformer Models\n",
"\n",
"**BERT** (Bidirectional Encoder Representations from Transformers) е много голяма многослойна трансформаторна мрежа с 12 слоя за *BERT-base* и 24 за *BERT-large*. Моделът първо се предварително обучава върху голям корпус от текстови данни (WikiPedia + книги) чрез обучение без надзор (предсказване на маскирани думи в изречение). По време на предварителното обучение моделът усвоява значително ниво на езиково разбиране, което след това може да бъде използвано с други набори от данни чрез фина настройка. Този процес се нарича **трансферно обучение**.\n",
"\n",
"![картина от http://jalammar.github.io/illustrated-bert/](../../../../../translated_images/jalammarBERT-language-modeling-masked-lm.34f113ea5fec4362e39ee4381aab7cad06b5465a0b5f053a0f2aa05fbe14e746.bg.png)\n",
"\n",
"Съществуват много вариации на трансформаторни архитектури, включително BERT, DistilBERT, BigBird, OpenGPT3 и други, които могат да бъдат фино настроени.\n",
"\n",
"Нека видим как можем да използваме предварително обучен модел BERT за решаване на нашия традиционен проблем с класификация на последователности. Ще заимстваме идеята и част от кода от [официалната документация](https://www.tensorflow.org/text/tutorials/classify_text_with_bert).\n",
"\n",
"За да заредим предварително обучени модели, ще използваме **Tensorflow hub**. Първо, нека заредим специфичния за BERT векторизатор:\n"
]
},
{
"cell_type": "code",
"execution_count": 1,
"metadata": {},
"outputs": [
{
"ename": "ModuleNotFoundError",
"evalue": "No module named 'tensorflow_text'",
"output_type": "error",
"traceback": [
"\u001b[1;31m---------------------------------------------------------------------------\u001b[0m",
"\u001b[1;31mModuleNotFoundError\u001b[0m Traceback (most recent call last)",
"\u001b[1;32m~\\AppData\\Local\\Temp/ipykernel_41180/4216669875.py\u001b[0m in \u001b[0;36m<module>\u001b[1;34m\u001b[0m\n\u001b[1;32m----> 1\u001b[1;33m \u001b[1;32mimport\u001b[0m \u001b[0mtensorflow_text\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m\u001b[0;32m 2\u001b[0m \u001b[1;32mimport\u001b[0m \u001b[0mtensorflow_hub\u001b[0m \u001b[1;32mas\u001b[0m \u001b[0mhub\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 3\u001b[0m \u001b[0mvectorizer\u001b[0m \u001b[1;33m=\u001b[0m \u001b[0mhub\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mKerasLayer\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;34m'https://tfhub.dev/tensorflow/bert_en_uncased_preprocess/3'\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n",
"\u001b[1;31mModuleNotFoundError\u001b[0m: No module named 'tensorflow_text'"
]
}
],
"source": [
"import tensorflow_text \n",
"import tensorflow_hub as hub\n",
"vectorizer = hub.KerasLayer('https://tfhub.dev/tensorflow/bert_en_uncased_preprocess/3')"
]
},
{
"cell_type": "code",
"execution_count": 7,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"{'input_type_ids': <tf.Tensor: shape=(1, 128), dtype=int32, numpy=\n",
" array([[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n",
" 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n",
" 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n",
" 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n",
" 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n",
" 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]],\n",
" dtype=int32)>,\n",
" 'input_word_ids': <tf.Tensor: shape=(1, 128), dtype=int32, numpy=\n",
" array([[ 101, 1045, 2293, 19081, 102, 0, 0, 0, 0,\n",
" 0, 0, 0, 0, 0, 0, 0, 0, 0,\n",
" 0, 0, 0, 0, 0, 0, 0, 0, 0,\n",
" 0, 0, 0, 0, 0, 0, 0, 0, 0,\n",
" 0, 0, 0, 0, 0, 0, 0, 0, 0,\n",
" 0, 0, 0, 0, 0, 0, 0, 0, 0,\n",
" 0, 0, 0, 0, 0, 0, 0, 0, 0,\n",
" 0, 0, 0, 0, 0, 0, 0, 0, 0,\n",
" 0, 0, 0, 0, 0, 0, 0, 0, 0,\n",
" 0, 0, 0, 0, 0, 0, 0, 0, 0,\n",
" 0, 0, 0, 0, 0, 0, 0, 0, 0,\n",
" 0, 0, 0, 0, 0, 0, 0, 0, 0,\n",
" 0, 0, 0, 0, 0, 0, 0, 0, 0,\n",
" 0, 0, 0, 0, 0, 0, 0, 0, 0,\n",
" 0, 0]], dtype=int32)>,\n",
" 'input_mask': <tf.Tensor: shape=(1, 128), dtype=int32, numpy=\n",
" array([[1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n",
" 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n",
" 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n",
" 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n",
" 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n",
" 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]],\n",
" dtype=int32)>}"
]
},
"execution_count": 7,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"vectorizer(['I love transformers'])"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Важно е да използвате същия векторизатор, който е бил използван за обучение на оригиналната мрежа. Освен това, BERT векторизаторът връща три компонента:\n",
"* `input_word_ids`, което е последователност от номера на токени за входното изречение\n",
"* `input_mask`, показваща коя част от последователността съдържа действителния вход и коя е запълване. Това е подобно на маската, произведена от слоя `Masking`\n",
"* `input_type_ids` се използва за задачи по езиково моделиране и позволява да се специфицират две входни изречения в една последователност.\n",
"\n",
"След това можем да създадем екстрактор на характеристики BERT:\n"
]
},
{
"cell_type": "code",
"execution_count": 8,
"metadata": {},
"outputs": [],
"source": [
"bert = hub.KerasLayer('https://tfhub.dev/tensorflow/small_bert/bert_en_uncased_L-4_H-128_A-2/1')"
]
},
{
"cell_type": "code",
"execution_count": 9,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"pooled_output -> (1, 128)\n",
"encoder_outputs -> 4\n",
"sequence_output -> (1, 128, 128)\n",
"default -> (1, 128)\n"
]
}
],
"source": [
"z = bert(vectorizer(['I love transformers']))\n",
"for i,x in z.items():\n",
" print(f\"{i} -> { len(x) if isinstance(x, list) else x.shape }\")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Така че, BERT слой връща няколко полезни резултата:\n",
"* `pooled_output` е резултат от осредняване на всички токени в последователността. Можете да го разглеждате като интелигентно семантично вграждане на цялата мрежа. Той е еквивалентен на изхода на слоя `GlobalAveragePooling1D` в предишния ни модел.\n",
"* `sequence_output` е изходът на последния трансформър слой (съответства на изхода на `TransformerBlock` в модела ни по-горе).\n",
"* `encoder_outputs` са изходите на всички трансформър слоеве. Тъй като сме заредили BERT модел с 4 слоя (както вероятно можете да предположите от името, което съдържа `4_H`), той има 4 тензора. Последният е същият като `sequence_output`.\n",
"\n",
"Сега ще дефинираме крайния модел за класификация. Ще използваме *функционално дефиниране на модела*, при което дефинираме входа на модела и след това предоставяме серия от изрази за изчисляване на неговия изход. Също така ще направим теглата на BERT модела не-трениращи и ще тренираме само крайния класификатор:\n"
]
},
{
"cell_type": "code",
"execution_count": 10,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Model: \"model\"\n",
"__________________________________________________________________________________________________\n",
"Layer (type) Output Shape Param # Connected to \n",
"==================================================================================================\n",
"input_1 (InputLayer) [(None,)] 0 \n",
"__________________________________________________________________________________________________\n",
"keras_layer (KerasLayer) {'input_type_ids': ( 0 input_1[0][0] \n",
"__________________________________________________________________________________________________\n",
"keras_layer_1 (KerasLayer) {'pooled_output': (N 4782465 keras_layer[0][0] \n",
" keras_layer[0][1] \n",
" keras_layer[0][2] \n",
"__________________________________________________________________________________________________\n",
"dropout_4 (Dropout) (None, 128) 0 keras_layer_1[0][5] \n",
"__________________________________________________________________________________________________\n",
"dense_4 (Dense) (None, 4) 516 dropout_4[0][0] \n",
"==================================================================================================\n",
"Total params: 4,782,981\n",
"Trainable params: 516\n",
"Non-trainable params: 4,782,465\n",
"__________________________________________________________________________________________________\n"
]
}
],
"source": [
"inp = keras.Input(shape=(),dtype=tf.string)\n",
"x = vectorizer(inp)\n",
"x = bert(x)\n",
"x = keras.layers.Dropout(0.1)(x['pooled_output'])\n",
"out = keras.layers.Dense(4,activation='softmax')(x)\n",
"model = keras.models.Model(inp,out)\n",
"bert.trainable = False\n",
"model.summary()"
]
},
{
"cell_type": "code",
"execution_count": 11,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"938/938 [==============================] - 528s 559ms/step - loss: 0.8056 - acc: 0.6983 - val_loss: 0.5953 - val_acc: 0.7888\n"
]
},
{
"data": {
"text/plain": [
"<tensorflow.python.keras.callbacks.History at 0x7f9bb1e36d00>"
]
},
"execution_count": 11,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"model.compile(loss='sparse_categorical_crossentropy',metrics=['acc'], optimizer='adam')\n",
"model.fit(ds_train.map(tupelize).batch(128),validation_data=ds_test.map(tupelize).batch(128))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Въпреки че има малко параметри за обучение, процесът е доста бавен, защото екстракторът на характеристики на BERT е изчислително тежък. Изглежда, че не успяхме да постигнем разумна точност, или поради липса на обучение, или поради недостатъчни параметри на модела.\n",
"\n",
"Нека опитаме да отключим теглата на BERT и да го обучим също. Това изисква много малка скорост на обучение, както и по-внимателна стратегия за обучение с **затопляне**, използвайки оптимизатора **AdamW**. Ще използваме пакета `tf-models-official`, за да създадем оптимизатора:\n"
]
},
{
"cell_type": "code",
"execution_count": 12,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Model: \"model\"\n",
"__________________________________________________________________________________________________\n",
"Layer (type) Output Shape Param # Connected to \n",
"==================================================================================================\n",
"input_1 (InputLayer) [(None,)] 0 \n",
"__________________________________________________________________________________________________\n",
"keras_layer (KerasLayer) {'input_type_ids': ( 0 input_1[0][0] \n",
"__________________________________________________________________________________________________\n",
"keras_layer_1 (KerasLayer) {'pooled_output': (N 4782465 keras_layer[0][0] \n",
" keras_layer[0][1] \n",
" keras_layer[0][2] \n",
"__________________________________________________________________________________________________\n",
"dropout_4 (Dropout) (None, 128) 0 keras_layer_1[0][5] \n",
"__________________________________________________________________________________________________\n",
"dense_4 (Dense) (None, 4) 516 dropout_4[0][0] \n",
"==================================================================================================\n",
"Total params: 4,782,981\n",
"Trainable params: 4,782,980\n",
"Non-trainable params: 1\n",
"__________________________________________________________________________________________________\n",
"938/938 [==============================] - 629s 664ms/step - loss: 0.6344 - acc: 0.7658 - val_loss: 0.4876 - val_acc: 0.8247\n"
]
},
{
"data": {
"text/plain": [
"<tensorflow.python.keras.callbacks.History at 0x7f9bb0bd0070>"
]
},
"execution_count": 12,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"from official.nlp import optimization \n",
"bert.trainable=True\n",
"model.summary()\n",
"epochs = 3\n",
"opt = optimization.create_optimizer(\n",
" init_lr=3e-5,\n",
" num_train_steps=epochs*len(ds_train),\n",
" num_warmup_steps=0.1*epochs*len(ds_train),\n",
" optimizer_type='adamw')\n",
"\n",
"model.compile(loss='sparse_categorical_crossentropy',metrics=['acc'], optimizer=opt)\n",
"model.fit(ds_train.map(tupelize).batch(128),validation_data=ds_test.map(tupelize).batch(128))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Както виждате, обучението върви доста бавно - но може да искате да експериментирате и да обучите модела за няколко епохи (5-10), за да видите дали можете да постигнете по-добър резултат в сравнение с подходите, които сме използвали преди.\n",
"\n",
"## Библиотека Huggingface Transformers\n",
"\n",
"Друг много разпространен (и малко по-опростен) начин за използване на Transformer модели е [пакетът HuggingFace](https://github.com/huggingface/), който предоставя лесни за използване модули за различни NLP задачи. Той е достъпен както за Tensorflow, така и за PyTorch, друга много популярна рамка за невронни мрежи.\n",
"\n",
"> **Note**: Ако не се интересувате как работи библиотеката Transformers - можете да пропуснете до края на този ноутбук, защото няма да видите нещо съществено различно от това, което направихме по-горе. Ще повтаряме същите стъпки за обучение на BERT модел, използвайки различна библиотека и значително по-голям модел. Следователно, процесът включва доста дълго обучение, така че може просто да искате да прегледате кода.\n",
"\n",
"Нека видим как нашият проблем може да бъде решен с помощта на [Huggingface Transformers](http://huggingface.co).\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Първото нещо, което трябва да направим, е да изберем модела, който ще използваме. Освен някои вградени модели, Huggingface разполага с [онлайн хранилище за модели](https://huggingface.co/models), където можете да намерите още много предварително обучени модели, създадени от общността. Всички тези модели могат да бъдат заредени и използвани само чрез предоставяне на името на модела. Всички необходими двоични файлове за модела ще бъдат автоматично изтеглени.\n",
"\n",
"В определени случаи може да се наложи да заредите свои собствени модели. В този случай можете да посочите директорията, която съдържа всички свързани файлове, включително параметри за токенизатора, файла `config.json` с параметрите на модела, двоичните тегла и т.н.\n",
"\n",
"От името на модела можем да създадем както модела, така и токенизатора. Нека започнем с токенизатора:\n"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {},
"outputs": [],
"source": [
"import transformers\n",
"\n",
"# To load the model from Internet repository using model name. \n",
"# Use this if you are running from your own copy of the notebooks\n",
"bert_model = 'bert-base-uncased' \n",
"\n",
"# To load the model from the directory on disk. Use this for Microsoft Learn module, because we have\n",
"# prepared all required files for you.\n",
"#bert_model = './bert'\n",
"\n",
"tokenizer = transformers.BertTokenizer.from_pretrained(bert_model)\n",
"\n",
"MAX_SEQ_LEN = 128\n",
"PAD_INDEX = tokenizer.convert_tokens_to_ids(tokenizer.pad_token)\n",
"UNK_INDEX = tokenizer.convert_tokens_to_ids(tokenizer.unk_token)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Обектът `tokenizer` съдържа функцията `encode`, която може да се използва директно за кодиране на текст:\n"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"[101, 23435, 12314, 2003, 1037, 2307, 7705, 2005, 17953, 2361, 102]"
]
},
"execution_count": 3,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"tokenizer.encode('Tensorflow is a great framework for NLP')"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Можем също да използваме токенизатор, за да кодираме последователност по начин, подходящ за подаване към модела, т.е. включително полетата `token_ids`, `input_mask` и др. Можем също да уточним, че искаме тензори на Tensorflow, като предоставим аргумента `return_tensors='tf'`:\n"
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"{'input_ids': <tf.Tensor: shape=(1, 5), dtype=int32, numpy=array([[ 101, 7592, 1010, 2045, 102]], dtype=int32)>, 'token_type_ids': <tf.Tensor: shape=(1, 5), dtype=int32, numpy=array([[0, 0, 0, 0, 0]], dtype=int32)>, 'attention_mask': <tf.Tensor: shape=(1, 5), dtype=int32, numpy=array([[1, 1, 1, 1, 1]], dtype=int32)>}"
]
},
"execution_count": 4,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"tokenizer(['Hello, there'],return_tensors='tf')"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"В нашия случай ще използваме предварително обучен BERT модел, наречен `bert-base-uncased`. *Uncased* означава, че моделът е нечувствителен към главни и малки букви.\n",
"\n",
"Когато обучаваме модела, трябва да предоставим токенизирана последователност като вход, и затова ще проектираме процес за обработка на данни. Тъй като `tokenizer.encode` е Python функция, ще използваме същия подход, както в последния модул, като я извикаме с помощта на `py_function`:\n"
]
},
{
"cell_type": "code",
"execution_count": 31,
"metadata": {},
"outputs": [],
"source": [
"def process(x):\n",
" return tokenizer.encode(x.numpy().decode('utf-8'),return_tensors='tf',padding='max_length',max_length=MAX_SEQ_LEN,truncation=True)[0]\n",
"\n",
"def process_fn(x):\n",
" s = x['title']+' '+x['description']\n",
" e = tf.py_function(process,inp=[s],Tout=(tf.int32))\n",
" e.set_shape(MAX_SEQ_LEN)\n",
" return e,x['label']"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Сега можем да заредим действителния модел, използвайки пакета `BertForSequenceClassification`. Това гарантира, че нашият модел вече има необходимата архитектура за класификация, включително крайния класификатор. Ще видите предупредително съобщение, което посочва, че теглата на крайния класификатор не са инициализирани и моделът ще изисква предварително обучение - това е напълно нормално, защото точно това ще направим!\n"
]
},
{
"cell_type": "code",
"execution_count": 32,
"metadata": {},
"outputs": [],
"source": [
"model = transformers.TFBertForSequenceClassification.from_pretrained(bert_model,num_labels=4,output_attentions=False)"
]
},
{
"cell_type": "code",
"execution_count": 33,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Model: \"tf_bert_for_sequence_classification_1\"\n",
"_________________________________________________________________\n",
"Layer (type) Output Shape Param # \n",
"=================================================================\n",
"bert (TFBertMainLayer) multiple 109482240 \n",
"_________________________________________________________________\n",
"dropout_75 (Dropout) multiple 0 \n",
"_________________________________________________________________\n",
"classifier (Dense) multiple 3076 \n",
"=================================================================\n",
"Total params: 109,485,316\n",
"Trainable params: 109,485,316\n",
"Non-trainable params: 0\n",
"_________________________________________________________________\n"
]
}
],
"source": [
"model.summary()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Както можете да видите от `summary()`, моделът съдържа почти 110 милиона параметри! Предполага се, че ако искаме проста задача за класификация върху сравнително малък набор от данни, не искаме да обучаваме основния слой на BERT:\n"
]
},
{
"cell_type": "code",
"execution_count": 34,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Model: \"tf_bert_for_sequence_classification_1\"\n",
"_________________________________________________________________\n",
"Layer (type) Output Shape Param # \n",
"=================================================================\n",
"bert (TFBertMainLayer) multiple 109482240 \n",
"_________________________________________________________________\n",
"dropout_75 (Dropout) multiple 0 \n",
"_________________________________________________________________\n",
"classifier (Dense) multiple 3076 \n",
"=================================================================\n",
"Total params: 109,485,316\n",
"Trainable params: 3,076\n",
"Non-trainable params: 109,482,240\n",
"_________________________________________________________________\n"
]
}
],
"source": [
"model.layers[0].trainable = False\n",
"model.summary()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Сега сме готови да започнем обучението!\n",
"\n",
"> **Забележка**: Обучението на пълномащабен BERT модел може да отнеме много време! Затова ще го обучим само за първите 32 партиди. Това е само за да покажем как се настройва обучението на модела. Ако искате да опитате пълномащабно обучение - просто премахнете параметрите `steps_per_epoch` и `validation_steps` и се подгответе да чакате!\n"
]
},
{
"cell_type": "code",
"execution_count": 30,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"32/32 [==============================] - 142s 4s/step - loss: 1.3896 - acc: 0.2500 - val_loss: 1.3863 - val_acc: 0.2480\n"
]
},
{
"data": {
"text/plain": [
"<tensorflow.python.keras.callbacks.History at 0x7f1d40a4b6a0>"
]
},
"execution_count": 30,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"model.compile('adam','sparse_categorical_crossentropy',['acc'])\n",
"tf.get_logger().setLevel('ERROR')\n",
"model.fit(ds_train.map(process_fn).batch(32),validation_data=ds_test.map(process_fn).batch(32),steps_per_epoch=32,validation_steps=2)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Ако увеличите броя на итерациите, изчакате достатъчно дълго и тренирате за няколко епохи, можете да очаквате, че класификацията с BERT ще ни даде най-добрата точност! Това е така, защото BERT вече разбира доста добре структурата на езика и ние трябва само да донастроим крайния класификатор. Въпреки това, тъй като BERT е голям модел, целият процес на обучение отнема много време и изисква сериозна изчислителна мощност! (GPU, и за предпочитане повече от един).\n",
"\n",
"> **Note:** В нашия пример използваме един от най-малките предварително обучени модели на BERT. Съществуват по-големи модели, които вероятно ще дадат по-добри резултати.\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Основни моменти\n",
"\n",
"В този модул разгледахме най-новите архитектури на модели, базирани на **transformers**. Приложихме ги за нашата задача за класификация на текст, но по същия начин моделите BERT могат да се използват за извличане на обекти, отговаряне на въпроси и други задачи в областта на обработката на естествен език (NLP).\n",
"\n",
"Моделите на трансформъри представляват най-съвременното постижение в NLP и в повечето случаи трябва да бъдат първото решение, с което започвате да експериментирате при внедряване на персонализирани NLP решения. Въпреки това, разбирането на основните принципи на рекурентните невронни мрежи, обсъдени в този модул, е изключително важно, ако искате да изграждате усъвършенствани невронни модели.\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": []
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"\n---\n\n**Отказ от отговорност**: \nТози документ е преведен с помощта на AI услуга за превод [Co-op Translator](https://github.com/Azure/co-op-translator). Въпреки че се стремим към точност, моля, имайте предвид, че автоматизираните преводи може да съдържат грешки или неточности. Оригиналният документ на неговия роден език трябва да се счита за авторитетен източник. За критична информация се препоръчва професионален човешки превод. Ние не носим отговорност за недоразумения или погрешни интерпретации, произтичащи от използването на този превод.\n"
]
}
],
"metadata": {
"interpreter": {
"hash": "0cb620c6d4b9f7a635928804c26cf22403d89d98d79684e4529119355ee6d5a5"
},
"kernelspec": {
"display_name": "py38_tensorflow",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.8.12"
},
"coopTranslator": {
"original_hash": "ab59c532409774988ab875f2260e8e53",
"translation_date": "2025-08-30T00:49:38+00:00",
"source_file": "lessons/5-NLP/18-Transformers/TransformersTF.ipynb",
"language_code": "bg"
}
},
"nbformat": 4,
"nbformat_minor": 4
}

View File

@ -0,0 +1,492 @@
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Разпознаване на именувани обекти (NER)\n",
"\n",
"Този ноутбук е част от [Учебната програма за начинаещи в изкуствения интелект](http://aka.ms/ai-beginners).\n",
"\n",
"В този пример ще научим как да обучим модел за разпознаване на именувани обекти (NER) върху [Анотиран корпус за разпознаване на именувани обекти](https://www.kaggle.com/datasets/abhinavwalia95/entity-annotated-corpus) от Kaggle. Преди да продължите, моля, изтеглете файла [ner_dataset.csv](https://www.kaggle.com/datasets/abhinavwalia95/entity-annotated-corpus?resource=download&select=ner_dataset.csv) в текущата директория.\n"
]
},
{
"cell_type": "code",
"execution_count": 62,
"metadata": {},
"outputs": [],
"source": [
"import pandas as pd\n",
"from tensorflow import keras\n",
"import numpy as np"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Подготовка на набора от данни\n",
"\n",
"Ще започнем, като заредим набора от данни в dataframe. Ако искате да научите повече за използването на Pandas, посетете [урок за обработка на данни](https://github.com/microsoft/Data-Science-For-Beginners/tree/main/2-Working-With-Data/07-python) в нашия [Data Science for Beginners](http://aka.ms/datascience-beginners).\n"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
"<div>\n",
"<style scoped>\n",
" .dataframe tbody tr th:only-of-type {\n",
" vertical-align: middle;\n",
" }\n",
"\n",
" .dataframe tbody tr th {\n",
" vertical-align: top;\n",
" }\n",
"\n",
" .dataframe thead th {\n",
" text-align: right;\n",
" }\n",
"</style>\n",
"<table border=\"1\" class=\"dataframe\">\n",
" <thead>\n",
" <tr style=\"text-align: right;\">\n",
" <th></th>\n",
" <th>Sentence #</th>\n",
" <th>Word</th>\n",
" <th>POS</th>\n",
" <th>Tag</th>\n",
" </tr>\n",
" </thead>\n",
" <tbody>\n",
" <tr>\n",
" <th>0</th>\n",
" <td>Sentence: 1</td>\n",
" <td>Thousands</td>\n",
" <td>NNS</td>\n",
" <td>O</td>\n",
" </tr>\n",
" <tr>\n",
" <th>1</th>\n",
" <td>NaN</td>\n",
" <td>of</td>\n",
" <td>IN</td>\n",
" <td>O</td>\n",
" </tr>\n",
" <tr>\n",
" <th>2</th>\n",
" <td>NaN</td>\n",
" <td>demonstrators</td>\n",
" <td>NNS</td>\n",
" <td>O</td>\n",
" </tr>\n",
" <tr>\n",
" <th>3</th>\n",
" <td>NaN</td>\n",
" <td>have</td>\n",
" <td>VBP</td>\n",
" <td>O</td>\n",
" </tr>\n",
" <tr>\n",
" <th>4</th>\n",
" <td>NaN</td>\n",
" <td>marched</td>\n",
" <td>VBN</td>\n",
" <td>O</td>\n",
" </tr>\n",
" </tbody>\n",
"</table>\n",
"</div>"
],
"text/plain": [
" Sentence # Word POS Tag\n",
"0 Sentence: 1 Thousands NNS O\n",
"1 NaN of IN O\n",
"2 NaN demonstrators NNS O\n",
"3 NaN have VBP O\n",
"4 NaN marched VBN O"
]
},
"execution_count": 3,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"df = pd.read_csv('ner_dataset.csv',encoding='unicode-escape')\n",
"df.head()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Нека получим уникални тагове и създадем речници за търсене, които можем да използваме за преобразуване на таговете в номера на класове:\n"
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"array(['O', 'B-geo', 'B-gpe', 'B-per', 'I-geo', 'B-org', 'I-org', 'B-tim',\n",
" 'B-art', 'I-art', 'I-per', 'I-gpe', 'I-tim', 'B-nat', 'B-eve',\n",
" 'I-eve', 'I-nat'], dtype=object)"
]
},
"execution_count": 4,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"tags = df.Tag.unique()\n",
"tags"
]
},
{
"cell_type": "code",
"execution_count": 8,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"'O'"
]
},
"execution_count": 8,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"id2tag = dict(enumerate(tags))\n",
"tag2id = { v : k for k,v in id2tag.items() }\n",
"\n",
"id2tag[0]"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Сега трябва да направим същото с речника. За простота ще създадем речник, без да вземаме предвид честотата на думите; в реалния живот може да искате да използвате Keras vectorizer и да ограничите броя на думите.\n"
]
},
{
"cell_type": "code",
"execution_count": 14,
"metadata": {},
"outputs": [],
"source": [
"vocab = set(df['Word'].apply(lambda x: x.lower()))\n",
"id2word = { i+1 : v for i,v in enumerate(vocab) }\n",
"id2word[0] = '<UNK>'\n",
"vocab.add('<UNK>')\n",
"word2id = { v : k for k,v in id2word.items() }"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Трябва да създадем набор от данни с изречения за обучение. Нека преминем през оригиналния набор от данни и разделим всички отделни изречения на `X` (списъци от думи) и `Y` (списък от токени):\n"
]
},
{
"cell_type": "code",
"execution_count": 41,
"metadata": {},
"outputs": [],
"source": [
"X,Y = [],[]\n",
"s,t = [],[]\n",
"for i,row in df[['Sentence #','Word','Tag']].iterrows():\n",
" if pd.isna(row['Sentence #']):\n",
" s.append(row['Word'])\n",
" t.append(row['Tag'])\n",
" else:\n",
" if len(s)>0:\n",
" X.append(s)\n",
" Y.append(t)\n",
" s,t = [row['Word']],[row['Tag']]\n",
"X.append(s)\n",
"Y.append(t)\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": []
},
{
"cell_type": "code",
"execution_count": 93,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"([10386,\n",
" 23515,\n",
" 4134,\n",
" 29620,\n",
" 7954,\n",
" 13583,\n",
" 21193,\n",
" 12222,\n",
" 27322,\n",
" 18258,\n",
" 5815,\n",
" 15880,\n",
" 5355,\n",
" 25242,\n",
" 31327,\n",
" 18258,\n",
" 27067,\n",
" 23515,\n",
" 26444,\n",
" 14412,\n",
" 358,\n",
" 26551,\n",
" 5011,\n",
" 30558],\n",
" [0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0])"
]
},
"execution_count": 93,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"def vectorize(seq):\n",
" return [word2id[x.lower()] for x in seq]\n",
"\n",
"def tagify(seq):\n",
" return [tag2id[x] for x in seq]\n",
"\n",
"Xv = list(map(vectorize,X))\n",
"Yv = list(map(tagify,Y))\n",
"\n",
"Xv[0], Yv[0]"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"За простота ще допълним всички изречения с 0 токени до максималната дължина. В реалния живот може да искаме да използваме по-умна стратегия и да допълваме последователности само в рамките на един минипакет.\n"
]
},
{
"cell_type": "code",
"execution_count": 51,
"metadata": {},
"outputs": [],
"source": [
"X_data = keras.preprocessing.sequence.pad_sequences(Xv,padding='post')\n",
"Y_data = keras.preprocessing.sequence.pad_sequences(Yv,padding='post')"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Дефиниране на мрежа за класификация на токени\n",
"\n",
"Ще използваме двуслойна двупосочна LSTM мрежа за класификация на токени. За да приложим плътен класификатор към всеки от изходите на последния LSTM слой, ще използваме конструкцията `TimeDistributed`, която репликира същия плътен слой към всеки от изходите на LSTM на всяка стъпка:\n"
]
},
{
"cell_type": "code",
"execution_count": 94,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Model: \"sequential_3\"\n",
"_________________________________________________________________\n",
" Layer (type) Output Shape Param # \n",
"=================================================================\n",
" embedding_4 (Embedding) (None, 104, 300) 9545400 \n",
" \n",
" bidirectional_6 (Bidirectio (None, 104, 200) 320800 \n",
" nal) \n",
" \n",
" bidirectional_7 (Bidirectio (None, 104, 200) 240800 \n",
" nal) \n",
" \n",
" time_distributed_3 (TimeDis (None, 104, 17) 3417 \n",
" tributed) \n",
" \n",
"=================================================================\n",
"Total params: 10,110,417\n",
"Trainable params: 10,110,417\n",
"Non-trainable params: 0\n",
"_________________________________________________________________\n"
]
}
],
"source": [
"maxlen = X_data.shape[1]\n",
"vocab_size = len(vocab)\n",
"num_tags = len(tags)\n",
"model = keras.models.Sequential([\n",
" keras.layers.Embedding(vocab_size, 300, input_length=maxlen),\n",
" keras.layers.Bidirectional(keras.layers.LSTM(units=100, activation='tanh', return_sequences=True)),\n",
" keras.layers.Bidirectional(keras.layers.LSTM(units=100, activation='tanh', return_sequences=True)),\n",
" keras.layers.TimeDistributed(keras.layers.Dense(num_tags, activation='softmax'))\n",
"])\n",
"model.compile(loss='sparse_categorical_crossentropy',optimizer='adam',metrics=['acc'])\n",
"model.summary()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Имайте предвид, че тук изрично задаваме `maxlen` за нашия набор от данни - ако искаме мрежата да може да обработва последователности с променлива дължина, трябва да бъдем малко по-умни при дефинирането на мрежата.\n",
"\n",
"Сега нека обучим модела. За по-бърза работа ще обучаваме само за една епоха, но можете да опитате да обучавате за по-дълго време. Също така, може да искате да отделите част от набора от данни като тренировъчен набор, за да наблюдавате точността на валидацията.\n"
]
},
{
"cell_type": "code",
"execution_count": 57,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"1499/1499 [==============================] - 740s 488ms/step - loss: 0.0667 - acc: 0.9841\n"
]
},
{
"data": {
"text/plain": [
"<keras.callbacks.History at 0x16f0bb2a310>"
]
},
"execution_count": 57,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"model.fit(X_data,Y_data)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Тестване на резултата\n",
"\n",
"Нека сега видим как работи нашият модел за разпознаване на обекти върху примерно изречение:\n"
]
},
{
"cell_type": "code",
"execution_count": 91,
"metadata": {},
"outputs": [],
"source": [
"sent = 'John Smith went to Paris to attend a conference in cancer development institute'\n",
"words = sent.lower().split()\n",
"v = keras.preprocessing.sequence.pad_sequences([[word2id[x] for x in words]],padding='post',maxlen=maxlen)\n",
"res = model(v)[0]"
]
},
{
"cell_type": "code",
"execution_count": 92,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"john -> B-per\n",
"smith -> I-per\n",
"went -> O\n",
"to -> O\n",
"paris -> B-geo\n",
"to -> O\n",
"attend -> O\n",
"a -> O\n",
"conference -> O\n",
"in -> O\n",
"cancer -> B-org\n",
"development -> I-org\n",
"institute -> I-org\n"
]
}
],
"source": [
"r = np.argmax(res.numpy(),axis=1)\n",
"for i,w in zip(r,words):\n",
" print(f\"{w} -> {id2tag[i]}\")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Основни изводи\n",
"\n",
"Дори прост LSTM модел показва задоволителни резултати при NER. Въпреки това, за да постигнете значително по-добри резултати, може да искате да използвате големи предварително обучени езикови модели като BERT. Обучението на BERT за NER с помощта на библиотеката Huggingface Transformers е описано [тук](https://huggingface.co/course/chapter7/2?fw=pt).\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"\n---\n\n**Отказ от отговорност**: \nТози документ е преведен с помощта на AI услуга за превод [Co-op Translator](https://github.com/Azure/co-op-translator). Въпреки че се стремим към точност, моля, имайте предвид, че автоматизираните преводи може да съдържат грешки или неточности. Оригиналният документ на неговия роден език трябва да се счита за авторитетен източник. За критична информация се препоръчва професионален човешки превод. Ние не носим отговорност за недоразумения или погрешни интерпретации, произтичащи от използването на този превод.\n"
]
}
],
"metadata": {
"interpreter": {
"hash": "16af2a8bbb083ea23e5e41c7f5787656b2ce26968575d8763f2c4b17f9cd711f"
},
"kernelspec": {
"display_name": "Python 3.8.12 ('py38')",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.8.12"
},
"orig_nbformat": 4,
"coopTranslator": {
"original_hash": "254d25052dcca4ef84f59a05f2935bdc",
"translation_date": "2025-08-30T00:53:24+00:00",
"source_file": "lessons/5-NLP/19-NER/NER-TF.ipynb",
"language_code": "bg"
}
},
"nbformat": 4,
"nbformat_minor": 2
}

View File

@ -0,0 +1,325 @@
{
"cells": [
{
"attachments": {},
"cell_type": "markdown",
"metadata": {},
"source": [
"## Експериментиране с OpenAI GPT\n",
"\n",
"Този тефтер е част от [Учебната програма за начинаещи в ИИ](http://aka.ms/ai-beginners).\n",
"\n",
"В този тефтер ще разгледаме как можем да експериментираме с модела OpenAI-GPT, използвайки библиотеката `transformers` на Hugging Face.\n",
"\n",
"Без да губим време, нека създадем pipeline за генериране на текст и да започнем да генерираме!\n"
]
},
{
"cell_type": "code",
"execution_count": 1,
"metadata": {},
"outputs": [
{
"name": "stderr",
"output_type": "stream",
"text": [
"c:\\Users\\bethanycheum\\Desktop\\AI-For-Beginners\\.venv\\lib\\site-packages\\tqdm\\auto.py:21: TqdmWarning: IProgress not found. Please update jupyter and ipywidgets. See https://ipywidgets.readthedocs.io/en/stable/user_install.html\n",
" from .autonotebook import tqdm as notebook_tqdm\n",
"Downloading model.safetensors: 100%|██████████| 479M/479M [04:28<00:00, 1.78MB/s] \n",
"c:\\Users\\bethanycheum\\Desktop\\AI-For-Beginners\\.venv\\lib\\site-packages\\huggingface_hub\\file_download.py:133: UserWarning: `huggingface_hub` cache-system uses symlinks by default to efficiently store duplicated files but your machine does not support them in C:\\Users\\bethanycheum\\.cache\\huggingface\\hub. Caching files will still work but in a degraded version that might require more space on your disk. This warning can be disabled by setting the `HF_HUB_DISABLE_SYMLINKS_WARNING` environment variable. For more details, see https://huggingface.co/docs/huggingface_hub/how-to-cache#limitations.\n",
"To support symlinks on Windows, you either need to activate Developer Mode or to run Python as an administrator. In order to see activate developer mode, see this article: https://docs.microsoft.com/en-us/windows/apps/get-started/enable-your-device-for-development\n",
" warnings.warn(message)\n",
"Some weights of OpenAIGPTLMHeadModel were not initialized from the model checkpoint at openai-gpt and are newly initialized: ['position_ids']\n",
"You should probably TRAIN this model on a down-stream task to be able to use it for predictions and inference.\n",
"Downloading (…)neration_config.json: 100%|██████████| 74.0/74.0 [00:00<00:00, 48.8kB/s]\n",
"Downloading (…)olve/main/vocab.json: 100%|██████████| 816k/816k [00:00<00:00, 1.76MB/s]\n",
"Downloading (…)olve/main/merges.txt: 100%|██████████| 458k/458k [00:00<00:00, 1.11MB/s]\n",
"Downloading (…)/main/tokenizer.json: 100%|██████████| 1.27M/1.27M [00:00<00:00, 2.12MB/s]\n",
"Xformers is not installed correctly. If you want to use memory_efficient_attention to accelerate training use the following command to install Xformers\n",
"pip install xformers.\n"
]
},
{
"data": {
"text/plain": [
"[{'generated_text': \"Hello! I am a neural network, and I want to say that i apologize for not coming to you yourself, for not helping you, and that i was too busy getting dressed and studying for a midterm. you know, the kind where the teachers are like that and they come in pairs with their boyfriends, but not with theirs. it's true, that i have had a girlfriend, and i'm only going on wednesdays and thursdays because i was too busy with college, but maybe\"},\n",
" {'generated_text': 'Hello! I am a neural network, and I want to say that we have been blessed with a wonderful gift ; no one of us has died at all. and our spirits are strong, very strong. in one very lucky moment of luck for you, all has been given direction and destiny, and for us there are no more mysteries. the earth has been chosen for you, and that earth is now ours, and you must be forever in our hearts. \" \\n the words, as one,'},\n",
" {'generated_text': 'Hello! I am a neural network, and I want to say that if you would just turn and face the general, you would have a nice day. \" \\n \" sure thing, \" said one of the soldiers, and started to run. the rest of the soldiers followed, shouting. the general turned to general zulu, raising his arm. the general said something in his native language, and the general immediately started to run. zulu started to move toward the wall, with the'},\n",
" {'generated_text': 'Hello! I am a neural network, and I want to say that i am not a doctor but an anthropologist to you, a specialist, a specialist in the field of astrobiological biology, and that i am very much involved in this investigation. i am not sure, i am not certain, but i can confirm your conclusions and therefore i will go to the top. i have a colleague who has just returned from this expedition and his findings confirm that you are a specialist. that is, he'},\n",
" {'generated_text': \"Hello! I am a neural network, and I want to say that everyone here is in agreement that no matter how many times i say to myself,'he was never a man of action on the battlefield,'or'he 'll never take a chance at killing any civilians,'or'he 'll never let his men go undefended against enemy forces of this caliber,'or'that's just what i need in a day like today. \\n you see, there are only three groups that\"}]"
]
},
"execution_count": 1,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"from transformers import pipeline\n",
"\n",
"model_name = 'openai-gpt' \n",
"\n",
"generator = pipeline('text-generation', model=model_name)\n",
"\n",
"generator(\"Hello! I am a neural network, and I want to say that\", max_length=100, num_return_sequences=5)\n"
]
},
{
"attachments": {},
"cell_type": "markdown",
"metadata": {},
"source": [
"## Проектиране на подканите\n",
"\n",
"При някои от проблемите можете директно да използвате openai-gpt, като създадете правилните подканящи фрази. Разгледайте примерите по-долу:\n"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"[{'generated_text': 'Synonyms of a word cat: the same cat i used to stare at, and you in'},\n",
" {'generated_text': 'Synonyms of a word cat: cat of the woods, cat of the hills, cat of'},\n",
" {'generated_text': 'Synonyms of a word cat: you! \\n \" it\\'s a girl. \" i said'},\n",
" {'generated_text': \"Synonyms of a word cat: big cat. but how come, we didn't hear it\"},\n",
" {'generated_text': 'Synonyms of a word cat: \" mea - o - c \" which makes them sound'}]"
]
},
"execution_count": 2,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"generator(\"Synonyms of a word cat:\", max_length=20, num_return_sequences=5)"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"[{'generated_text': 'I love when you say this -> Positive\\nI have myself -> Negative\\nThis is awful for you to say this -> positive this is so horrible - > positive that your brother is gay - >'},\n",
" {'generated_text': 'I love when you say this -> Positive\\nI have myself -> Negative\\nThis is awful for you to say this -> negative i will bring this on you -, < positive am i, i'},\n",
" {'generated_text': 'I love when you say this -> Positive\\nI have myself -> Negative\\nThis is awful for you to say this -> negative i have self - esteem i must take it - : \\n - -'},\n",
" {'generated_text': 'I love when you say this -> Positive\\nI have myself -> Negative\\nThis is awful for you to say this -> negative this is - : \\n if it were true that the devil would have'},\n",
" {'generated_text': \"I love when you say this -> Positive\\nI have myself -> Negative\\nThis is awful for you to say this -> positive i have you - > positive it's a bad thing, > positive\"}]"
]
},
"execution_count": 3,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"generator(\"I love when you say this -> Positive\\nI have myself -> Negative\\nThis is awful for you to say this ->\", max_length=40, num_return_sequences=5)"
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"[{'generated_text': 'Translate English to French: cat => chat, dog => chien, student => new and unusual. there were no more words to be'},\n",
" {'generated_text': 'Translate English to French: cat => chat, dog => chien, student => student \\n his eyes were huge in his lean face as'},\n",
" {'generated_text': \"Translate English to French: cat => chat, dog => chien, student => the teacher's words, their words, their words.\"}]"
]
},
"execution_count": 4,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"generator(\"Translate English to French: cat => chat, dog => chien, student => \", top_k=50, max_length=30, num_return_sequences=3)"
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"[{'generated_text': 'People who liked the movie The Matrix also liked it, and there was the movie of the first man after us. \\n i wanted to laugh at how stupid these stupid actors were. no, they were'},\n",
" {'generated_text': \"People who liked the movie The Matrix also liked the movie, and the film was the result. and that's when the man in the story was brought into reality, after a few decades. \\n a\"},\n",
" {'generated_text': 'People who liked the movie The Matrix also liked the movie the matrix, because there was a very old movie movie called the matrix, where there was a great super hero, and the super hero came out'},\n",
" {'generated_text': \"People who liked the movie The Matrix also liked the movie that didn't have a chance to pay cash, if they could afford it. most often they got a good deal and a lot of money,\"},\n",
" {'generated_text': \"People who liked the movie The Matrix also liked the movie, and i didn't seem to have the same problem. \\n i 'd met the other half of my family. i spent most of my time\"}]"
]
},
"execution_count": 5,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"generator(\"People who liked the movie The Matrix also liked \", max_length=40, num_return_sequences=5)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Стратегии за избор на текст\n",
"\n",
"Досега използвахме проста стратегия за избор, наречена **алчна** стратегия, при която следващата дума се избира въз основа на най-високата вероятност. Ето как работи:\n"
]
},
{
"cell_type": "code",
"execution_count": 6,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"[{'generated_text': 'It was early evening when I can back from work. I usually work late, but this time it was an exception. When I entered a room, I saw my friend, a young man, sprawled across the bed in his bed. \\n \" hi, i\\'m mike eptirard. \" \\n there was silence on the other side of the door. i listened for any trace of life but there was nothing. my heart began to pound, i was starting to sweat, i took out my wallet'},\n",
" {'generated_text': 'It was early evening when I can back from work. I usually work late, but this time it was an exception. When I entered a room, I saw my mother on the bed, hugging her legs to her chest and sobbing. i saw my dad and mother from the corner of my eye. \\n elfin face was covered in tears as i entered the room. my dad and mother also wept ; just as they did every other time i came to work. but this time, they had different faces'},\n",
" {'generated_text': 'It was early evening when I can back from work. I usually work late, but this time it was an exception. When I entered a room, I saw the room had changed because it was dark. it still smelled like a hospital. a new light shined through from a vent in the ceiling. i found myself in a bathroom and a small room with a sink and a wall of glass. the bathroom billion years ago. not so different from all of the rest of the apartment. \\n now...'},\n",
" {'generated_text': 'It was early evening when I can back from work. I usually work late, but this time it was an exception. When I entered a room, I saw a large woman with dark hair and pale skin. she was asleep, but i noticed a faint movement of her face. i could sense she was awake. i got up and walked over to her. \\n \" hello miss. i am inspector michael o\\'dell ; we are investigating the case against you. i wanted to ask if you were the'},\n",
" {'generated_text': 'It was early evening when I can back from work. I usually work late, but this time it was an exception. When I entered a room, I saw i had an empty table and three empty chairs. that was all i needed. i had left a note on a table in the center of the room and had a pen in hand. \" \\n \" i think what you were doing was something he was doing to her. \" \\n \" yeah, \" i nodded with a grin. \" i'}]"
]
},
"execution_count": 6,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"prompt = \"It was early evening when I can back from work. I usually work late, but this time it was an exception. When I entered a room, I saw\"\n",
"generator(prompt,max_length=100,num_return_sequences=5)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Beam Search** позволява на генератора да изследва няколко направления (*beams*) на генериране на текст и да избере тези с най-висока обща оценка. Можете да използвате beam search, като зададете параметъра `num_beams`. Можете също така да зададете `no_repeat_ngram_size`, за да накажете модела за повтаряне на n-грамове с даден размер:\n"
]
},
{
"cell_type": "code",
"execution_count": 7,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"[{'generated_text': 'It was early evening when I can back from work. I usually work late, but this time it was an exception. When I entered a room, I saw a man sitting in a chair with his head in his hands. he didn\\'t look up as i approached. \\n \" excuse me, sir, \" i said. \" can i help you? \" \\n the man looked up at me. his eyes were red - rimmed and his face was pale, as if he hadn\\'t slept in days'},\n",
" {'generated_text': 'It was early evening when I can back from work. I usually work late, but this time it was an exception. When I entered a room, I saw a man sitting at a desk in the middle of the room. he had his back to me, so i couldn\\'t see what he was doing. \" \\n \" what did he look like? \" i asked as i sat down on the bed next to her. \\n she took a deep breath and looked at me with tears in her eyes'},\n",
" {'generated_text': 'It was early evening when I can back from work. I usually work late, but this time it was an exception. When I entered a room, I saw a woman sitting on the bed, reading a book. she looked up at me and smiled. \\n \" hi, \" she said. \" can i help you? \" \\n i sat down next to her and looked around the room. the walls were white, and there was a large window in the middle of the wall that looked out on'},\n",
" {'generated_text': 'It was early evening when I can back from work. I usually work late, but this time it was an exception. When I entered a room, I saw a man sitting at a table in the middle of the room. he looked up as i walked in, and when he saw me, he got up and walked over to me. \\n \" can i help you? \" he asked as he put his hand on the small of my back and led me to a chair at the other end of'},\n",
" {'generated_text': 'It was early evening when I can back from work. I usually work late, but this time it was an exception. When I entered a room, I saw a woman sitting on the edge of her bed, reading a book. she looked up at me and smiled. \\n \" hello, \" she said. \" can i help you? \" \\n i didn\\'t know what to say, so i just sat down in the chair next to the bed and looked at her. her hair was dark brown'}]"
]
},
"execution_count": 7,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"prompt = \"It was early evening when I can back from work. I usually work late, but this time it was an exception. When I entered a room, I saw\"\n",
"generator(prompt,max_length=100,num_return_sequences=5,num_beams=10,no_repeat_ngram_size=2)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Избор на проба** избира следващата дума недетерминирано, използвайки разпределението на вероятностите, върнато от модела. Можете да включите избора на проба, като използвате параметъра `do_sample=True`. Можете също така да зададете `temperature`, за да направите модела по-малко или повече детерминиран.\n"
]
},
{
"cell_type": "code",
"execution_count": 8,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"[{'generated_text': 'It was early evening when I can back from work. I usually work late, but this time it was an exception. When I entered a room, I saw her. she was on the bed, but she looked very different. \\n \" honey, what\\'s the matter? \" i asked. \\n she sat up. \" i can\\'t believe it\\'s real. i\\'ve been dreaming about you for the last two days. \" \\n \" i can\\'t believe it either. i guess that\\'s how'}]"
]
},
"execution_count": 8,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"prompt = \"It was early evening when I can back from work. I usually work late, but this time it was an exception. When I entered a room, I saw\"\n",
"generator(prompt,max_length=100,do_sample=True,temperature=0.8)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Можем също да предоставим допълнителни параметри за семплиране:\n",
"* `top_k` определя броя на опциите за думи, които да се разгледат при използване на семплиране. Това минимизира шанса за получаване на странни (с ниска вероятност) думи в текста.\n",
"* `top_p` е подобен, но избираме най-малкия подмножество от най-вероятните думи, чиято обща вероятност е по-голяма от p.\n",
"\n",
"Не се колебайте да експериментирате с добавянето на тези параметри.\n"
]
},
{
"attachments": {},
"cell_type": "markdown",
"metadata": {},
"source": [
"## Настройка на вашите модели\n",
"\n",
"Можете също така да [настроите вашия модел](https://learn.microsoft.com/en-us/azure/cognitive-services/openai/how-to/fine-tuning?pivots=programming-language-studio?WT.mc_id=academic-77998-bethanycheum) върху ваш собствен набор от данни. Това ще ви позволи да коригирате стила на текста, като същевременно запазите основната част от езиковия модел.\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"\n---\n\n**Отказ от отговорност**: \nТози документ е преведен с помощта на AI услуга за превод [Co-op Translator](https://github.com/Azure/co-op-translator). Въпреки че се стремим към точност, моля, имайте предвид, че автоматизираните преводи може да съдържат грешки или неточности. Оригиналният документ на неговия роден език трябва да се счита за авторитетен източник. За критична информация се препоръчва професионален човешки превод. Ние не носим отговорност за недоразумения или погрешни интерпретации, произтичащи от използването на този превод.\n"
]
}
],
"metadata": {
"interpreter": {
"hash": "16af2a8bbb083ea23e5e41c7f5787656b2ce26968575d8763f2c4b17f9cd711f"
},
"kernelspec": {
"display_name": "Python 3.8.12 ('py38')",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.10.11"
},
"orig_nbformat": 4,
"coopTranslator": {
"original_hash": "d4ff89615d38924a55594f16d6d20678",
"translation_date": "2025-08-30T00:51:47+00:00",
"source_file": "lessons/5-NLP/20-LangModels/GPT-PyTorch.ipynb",
"language_code": "bg"
}
},
"nbformat": 4,
"nbformat_minor": 2
}

View File

@ -0,0 +1,49 @@
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Задача: Диофантови уравнения\n",
"\n",
"> Тази задача е част от [Учебната програма за начинаещи в AI](http://github.com/microsoft/ai-for-beginners) и е вдъхновена от [тази публикация](https://habr.com/post/128704/).\n",
"\n",
"Вашата цел е да решите така нареченото **Диофантово уравнение** - уравнение с цели корени и цели коефициенти. Например, разгледайте следното уравнение:\n",
"\n",
"$$a+2b+3c+4d=30$$\n",
"\n",
"Трябва да намерите цели корени $a$,$b$,$c$,$d\\in\\mathbb{N}$, които удовлетворяват това уравнение.\n",
"\n",
"Подсказки:\n",
"1. Можете да разгледате корените в интервала [0;30]\n",
"1. Като ген, обмислете използването на списък с стойности на корените\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": []
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"\n---\n\n**Отказ от отговорност**: \nТози документ е преведен с помощта на AI услуга за превод [Co-op Translator](https://github.com/Azure/co-op-translator). Въпреки че се стремим към точност, моля, имайте предвид, че автоматизираните преводи може да съдържат грешки или неточности. Оригиналният документ на неговия роден език трябва да се счита за авторитетен източник. За критична информация се препоръчва професионален човешки превод. Ние не носим отговорност за недоразумения или погрешни интерпретации, произтичащи от използването на този превод.\n"
]
}
],
"metadata": {
"language_info": {
"name": "python"
},
"orig_nbformat": 4,
"coopTranslator": {
"original_hash": "a967e1fa1e11ab2b6467b19349a4a9aa",
"translation_date": "2025-08-29T22:52:21+00:00",
"source_file": "lessons/6-Other/21-GeneticAlgorithms/Diophantine.ipynb",
"language_code": "bg"
}
},
"nbformat": 4,
"nbformat_minor": 2
}

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,501 @@
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Обучение на RL за балансиране на Cartpole\n",
"\n",
"Този ноутбук е част от [Учебната програма за начинаещи в ИИ](http://aka.ms/ai-beginners). Той е вдъхновен от [официалния урок на PyTorch](https://pytorch.org/tutorials/intermediate/reinforcement_q_learning.html) и [тази имплементация на Cartpole с PyTorch](https://github.com/yc930401/Actor-Critic-pytorch).\n",
"\n",
"В този пример ще използваме RL, за да обучим модел да балансира прът върху количка, която може да се движи наляво и надясно по хоризонтална ос. Ще използваме средата [OpenAI Gym](https://www.gymlibrary.ml/), за да симулираме пръта.\n",
"\n",
"> **Забележка**: Можете да изпълните кода от този урок локално (например чрез Visual Studio Code), като в този случай симулацията ще се отвори в нов прозорец. Ако изпълнявате кода онлайн, може да се наложи да направите някои промени в кода, както е описано [тук](https://towardsdatascience.com/rendering-openai-gym-envs-on-binder-and-google-colab-536f99391cc7).\n",
"\n",
"Ще започнем, като се уверим, че Gym е инсталиран:\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"import sys\n",
"!{sys.executable} -m pip install gym"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Сега нека създадем средата CartPole и да видим как да работим с нея. Една среда има следните свойства:\n",
"\n",
"* **Action space** е наборът от възможни действия, които можем да изпълним на всяка стъпка от симулацията\n",
"* **Observation space** е пространството на наблюденията, които можем да направим\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"import gym\n",
"\n",
"env = gym.make(\"CartPole-v1\")\n",
"\n",
"print(f\"Action space: {env.action_space}\")\n",
"print(f\"Observation space: {env.observation_space}\")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Нека видим как работи симулацията. Следният цикъл изпълнява симулацията, докато `env.step` не върне флага за прекратяване `done`. Ще избираме действия на случаен принцип, използвайки `env.action_space.sample()`, което означава, че експериментът вероятно ще се провали много бързо (средата CartPole приключва, когато скоростта на CartPole, неговата позиция или ъгъл излязат извън определени граници).\n",
"\n",
"> Симулацията ще се отвори в нов прозорец. Можете да изпълните кода няколко пъти и да наблюдавате как се държи.\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"env.reset()\n",
"\n",
"done = False\n",
"total_reward = 0\n",
"while not done:\n",
" env.render()\n",
" obs, rew, done, info = env.step(env.action_space.sample())\n",
" total_reward += rew\n",
" print(f\"{obs} -> {rew}\")\n",
"print(f\"Total reward: {total_reward}\")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Можете да забележите, че наблюденията съдържат 4 числа. Те са:\n",
"- Позиция на количката\n",
"- Скорост на количката\n",
"- Ъгъл на пръта\n",
"- Скорост на въртене на пръта\n",
"\n",
"`rew` е наградата, която получаваме на всяка стъпка. В средата CartPole можете да видите, че получавате 1 точка за всяка стъпка от симулацията, а целта е да максимизирате общата награда, т.е. времето, през което CartPole успява да се балансира, без да падне.\n",
"\n",
"По време на обучението чрез подсилване, нашата цел е да обучим **политика** $\\pi$, която за всяко състояние $s$ ще ни казва какво действие $a$ да предприемем, т.е. $a = \\pi(s)$.\n",
"\n",
"Ако искате вероятностно решение, можете да мислите за политиката като връщаща набор от вероятности за всяко действие, т.е. $\\pi(a|s)$ би означавало вероятността да предприемем действие $a$ в състояние $s$.\n",
"\n",
"## Метод на градиента на политиката\n",
"\n",
"В най-простия алгоритъм за обучение чрез подсилване, наречен **Градиент на политиката**, ще обучим невронна мрежа да предсказва следващото действие.\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"import numpy as np\n",
"import matplotlib.pyplot as plt\n",
"import torch\n",
"\n",
"num_inputs = 4\n",
"num_actions = 2\n",
"\n",
"model = torch.nn.Sequential(\n",
" torch.nn.Linear(num_inputs, 128, bias=False, dtype=torch.float32),\n",
" torch.nn.ReLU(),\n",
" torch.nn.Linear(128, num_actions, bias = False, dtype=torch.float32),\n",
" torch.nn.Softmax(dim=1)\n",
")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Ще обучим мрежата, като проведем много експерименти и актуализираме мрежата след всяко изпълнение. Нека дефинираме функция, която ще проведе експеримента и ще върне резултатите (т.нар. **следа**) - всички състояния, действия (и препоръчаните им вероятности) и награди:\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"def run_episode(max_steps_per_episode = 10000,render=False): \n",
" states, actions, probs, rewards = [],[],[],[]\n",
" state = env.reset()\n",
" for _ in range(max_steps_per_episode):\n",
" if render:\n",
" env.render()\n",
" action_probs = model(torch.from_numpy(np.expand_dims(state,0)))[0]\n",
" action = np.random.choice(num_actions, p=np.squeeze(action_probs.detach().numpy()))\n",
" nstate, reward, done, info = env.step(action)\n",
" if done:\n",
" break\n",
" states.append(state)\n",
" actions.append(action)\n",
" probs.append(action_probs.detach().numpy())\n",
" rewards.append(reward)\n",
" state = nstate\n",
" return np.vstack(states), np.vstack(actions), np.vstack(probs), np.vstack(rewards)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Можете да изпълните един епизод с нетренирана мрежа и да наблюдавате, че общата награда (или дължината на епизода) е много ниска:\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"s, a, p, r = run_episode()\n",
"print(f\"Total reward: {np.sum(r)}\")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Един от трудните аспекти на алгоритъма за градиент на политика е използването на **дисконтни награди**. Идеята е, че изчисляваме вектора на общите награди на всяка стъпка от играта и по време на този процес дисконтраме ранните награди, използвайки някакъв коефициент $gamma$. Също така нормализираме получения вектор, защото ще го използваме като тегло, за да повлияем на нашето обучение:\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"eps = 0.0001\n",
"\n",
"def discounted_rewards(rewards,gamma=0.99,normalize=True):\n",
" ret = []\n",
" s = 0\n",
" for r in rewards[::-1]:\n",
" s = r + gamma * s\n",
" ret.insert(0, s)\n",
" if normalize:\n",
" ret = (ret-np.mean(ret))/(np.std(ret)+eps)\n",
" return ret"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Сега да започнем истинското обучение! Ще проведем 300 епизода, като при всеки епизод ще изпълним следното:\n",
"\n",
"1. Провеждаме експеримента и събираме следата.\n",
"1. Изчисляваме разликата (`gradients`) между предприетите действия и предсказаните вероятности. Колкото по-малка е разликата, толкова по-уверени сме, че сме избрали правилното действие.\n",
"1. Изчисляваме дисконтирани награди и умножаваме градиентите по дисконтираните награди - това гарантира, че стъпките с по-високи награди ще имат по-голямо влияние върху крайния резултат в сравнение с тези с по-ниски награди.\n",
"1. Очакваните целеви действия за нашата невронна мрежа ще бъдат частично взети от предсказаните вероятности по време на изпълнението и частично от изчислените градиенти. Ще използваме параметъра `alpha`, за да определим до каква степен градиентите и наградите се вземат предвид - това се нарича *скорост на учене* на алгоритъма за подсилване.\n",
"1. Накрая обучаваме мрежата върху състоянията и очакваните действия и повтаряме процеса.\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"optimizer = torch.optim.Adam(model.parameters(), lr=0.01)\n",
"\n",
"def train_on_batch(x, y):\n",
" x = torch.from_numpy(x)\n",
" y = torch.from_numpy(y)\n",
" optimizer.zero_grad()\n",
" predictions = model(x)\n",
" loss = -torch.mean(torch.log(predictions) * y)\n",
" loss.backward()\n",
" optimizer.step()\n",
" return loss"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"alpha = 1e-4\n",
"\n",
"history = []\n",
"for epoch in range(300):\n",
" states, actions, probs, rewards = run_episode()\n",
" one_hot_actions = np.eye(2)[actions.T][0]\n",
" gradients = one_hot_actions-probs\n",
" dr = discounted_rewards(rewards)\n",
" gradients *= dr\n",
" target = alpha*np.vstack([gradients])+probs\n",
" train_on_batch(states,target)\n",
" history.append(np.sum(rewards))\n",
" if epoch%100==0:\n",
" print(f\"{epoch} -> {np.sum(rewards)}\")\n",
"\n",
"plt.plot(history)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Сега нека стартираме епизода с рендериране, за да видим резултата:\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"_ = run_episode(render=True)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Надявам се, че вече виждате, че пръчката може да се балансира доста добре!\n",
"\n",
"## Модел Actor-Critic\n",
"\n",
"Моделът Actor-Critic е по-нататъшно развитие на градиентите на политиката, при което изграждаме невронна мрежа, за да научим както политиката, така и оценените награди. Мрежата ще има два изхода (или можете да я разглеждате като две отделни мрежи):\n",
"* **Actor** ще препоръчва действието, което да се предприеме, като ни предоставя разпределението на вероятностите за състоянието, както в модела с градиенти на политиката.\n",
"* **Critic** ще оценява каква би била наградата от тези действия. Той връща общите оценени награди в бъдеще за даденото състояние.\n",
"\n",
"Нека дефинираме такъв модел:\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"from itertools import count\n",
"import torch.nn.functional as F"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"device = torch.device(\"cuda\" if torch.cuda.is_available() else \"cpu\")\n",
"env = gym.make(\"CartPole-v1\")\n",
"\n",
"state_size = env.observation_space.shape[0]\n",
"action_size = env.action_space.n\n",
"lr = 0.0001\n",
"\n",
"class Actor(torch.nn.Module):\n",
" def __init__(self, state_size, action_size):\n",
" super(Actor, self).__init__()\n",
" self.state_size = state_size\n",
" self.action_size = action_size\n",
" self.linear1 = torch.nn.Linear(self.state_size, 128)\n",
" self.linear2 = torch.nn.Linear(128, 256)\n",
" self.linear3 = torch.nn.Linear(256, self.action_size)\n",
"\n",
" def forward(self, state):\n",
" output = F.relu(self.linear1(state))\n",
" output = F.relu(self.linear2(output))\n",
" output = self.linear3(output)\n",
" distribution = torch.distributions.Categorical(F.softmax(output, dim=-1))\n",
" return distribution\n",
"\n",
"\n",
"class Critic(torch.nn.Module):\n",
" def __init__(self, state_size, action_size):\n",
" super(Critic, self).__init__()\n",
" self.state_size = state_size\n",
" self.action_size = action_size\n",
" self.linear1 = torch.nn.Linear(self.state_size, 128)\n",
" self.linear2 = torch.nn.Linear(128, 256)\n",
" self.linear3 = torch.nn.Linear(256, 1)\n",
"\n",
" def forward(self, state):\n",
" output = F.relu(self.linear1(state))\n",
" output = F.relu(self.linear2(output))\n",
" value = self.linear3(output)\n",
" return value"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Ще трябва леко да модифицираме функциите `discounted_rewards` и `run_episode`:\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"def discounted_rewards(next_value, rewards, masks, gamma=0.99):\n",
" R = next_value\n",
" returns = []\n",
" for step in reversed(range(len(rewards))):\n",
" R = rewards[step] + gamma * R * masks[step]\n",
" returns.insert(0, R)\n",
" return returns\n",
"\n",
"def run_episode(actor, critic, n_iters):\n",
" optimizerA = torch.optim.Adam(actor.parameters())\n",
" optimizerC = torch.optim.Adam(critic.parameters())\n",
" for iter in range(n_iters):\n",
" state = env.reset()\n",
" log_probs = []\n",
" values = []\n",
" rewards = []\n",
" masks = []\n",
" entropy = 0\n",
" env.reset()\n",
"\n",
" for i in count():\n",
" env.render()\n",
" state = torch.FloatTensor(state).to(device)\n",
" dist, value = actor(state), critic(state)\n",
"\n",
" action = dist.sample()\n",
" next_state, reward, done, _ = env.step(action.cpu().numpy())\n",
"\n",
" log_prob = dist.log_prob(action).unsqueeze(0)\n",
" entropy += dist.entropy().mean()\n",
"\n",
" log_probs.append(log_prob)\n",
" values.append(value)\n",
" rewards.append(torch.tensor([reward], dtype=torch.float, device=device))\n",
" masks.append(torch.tensor([1-done], dtype=torch.float, device=device))\n",
"\n",
" state = next_state\n",
"\n",
" if done:\n",
" print('Iteration: {}, Score: {}'.format(iter, i))\n",
" break\n",
"\n",
"\n",
" next_state = torch.FloatTensor(next_state).to(device)\n",
" next_value = critic(next_state)\n",
" returns = discounted_rewards(next_value, rewards, masks)\n",
"\n",
" log_probs = torch.cat(log_probs)\n",
" returns = torch.cat(returns).detach()\n",
" values = torch.cat(values)\n",
"\n",
" advantage = returns - values\n",
"\n",
" actor_loss = -(log_probs * advantage.detach()).mean()\n",
" critic_loss = advantage.pow(2).mean()\n",
"\n",
" optimizerA.zero_grad()\n",
" optimizerC.zero_grad()\n",
" actor_loss.backward()\n",
" critic_loss.backward()\n",
" optimizerA.step()\n",
" optimizerC.step()\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Сега ще изпълним основния цикъл за обучение. Ще използваме ръчен процес на обучение на мрежата чрез изчисляване на подходящи функции за загуба и актуализиране на параметрите на мрежата:\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"\n",
"actor = Actor(state_size, action_size).to(device)\n",
"critic = Critic(state_size, action_size).to(device)\n",
"run_episode(actor, critic, n_iters=100)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": []
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"env.close()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Основни моменти\n",
"\n",
"В тази демонстрация разгледахме два алгоритъма за подсилено обучение: прост градиент на политика и по-сложния актьор-критик. Можете да видите, че тези алгоритми работят с абстрактни понятия като състояние, действие и награда - което ги прави приложими в много различни среди.\n",
"\n",
"Подсиленото обучение ни позволява да научим най-добрата стратегия за решаване на даден проблем, като се основаваме единствено на крайната награда. Фактът, че не се нуждаем от етикетирани набори от данни, ни позволява да повтаряме симулациите многократно, за да оптимизираме нашите модели. Въпреки това, все още има много предизвикателства в подсиленото обучение, които можете да изучите, ако решите да се задълбочите в тази интересна област на изкуствения интелект.\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"\n---\n\n**Отказ от отговорност**: \nТози документ е преведен с помощта на AI услуга за превод [Co-op Translator](https://github.com/Azure/co-op-translator). Въпреки че се стремим към точност, моля, имайте предвид, че автоматизираните преводи може да съдържат грешки или неточности. Оригиналният документ на неговия роден език трябва да се счита за авторитетен източник. За критична информация се препоръчва професионален човешки превод. Ние не носим отговорност за недоразумения или погрешни интерпретации, произтичащи от използването на този превод.\n"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3.10.4 64-bit",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.10.4"
},
"orig_nbformat": 4,
"vscode": {
"interpreter": {
"hash": "916dbcbb3f70747c44a77c7bcd40155683ae19c65e1c03b4aa3499c5328201f1"
}
},
"coopTranslator": {
"original_hash": "04f8d9978cd11281d81dd037cbf6ce20",
"translation_date": "2025-08-29T22:59:31+00:00",
"source_file": "lessons/6-Other/22-DeepRL/CartPole-RL-PyTorch.ipynb",
"language_code": "bg"
}
},
"nbformat": 4,
"nbformat_minor": 2
}

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,109 @@
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Обучение на Mountain Car да избяга\n",
"\n",
"Задача от [AI for Beginners Curriculum](https://github.com/microsoft/ai-for-beginners).\n",
"\n",
"Вашата цел е да обучите RL агента да контролира [Mountain Car](https://www.gymlibrary.ml/environments/classic_control/mountain_car/) в OpenAI Environment.\n",
"\n",
"Нека започнем с създаването на средата:\n"
]
},
{
"cell_type": "code",
"execution_count": 1,
"metadata": {},
"outputs": [],
"source": [
"import gym\n",
"env = gym.make('MountainCar-v0')"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Нека видим как изглежда случайният експеримент:\n"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {},
"outputs": [],
"source": [
"state = env.reset()\n",
"while True:\n",
" env.render()\n",
" action = env.action_space.sample()\n",
" state, reward, done, info = env.step(action)\n",
" if done:\n",
" break"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": []
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"## Lost of code here"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {},
"outputs": [],
"source": [
"env.close()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"\n---\n\n**Отказ от отговорност**: \nТози документ е преведен с помощта на AI услуга за превод [Co-op Translator](https://github.com/Azure/co-op-translator). Въпреки че се стремим към точност, моля, имайте предвид, че автоматизираните преводи може да съдържат грешки или неточности. Оригиналният документ на неговия роден език трябва да се счита за авторитетен източник. За критична информация се препоръчва професионален човешки превод. Ние не носим отговорност за недоразумения или погрешни интерпретации, произтичащи от използването на този превод.\n"
]
}
],
"metadata": {
"interpreter": {
"hash": "16af2a8bbb083ea23e5e41c7f5787656b2ce26968575d8763f2c4b17f9cd711f"
},
"kernelspec": {
"display_name": "Python 3.8.12 ('py38')",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.8.12"
},
"coopTranslator": {
"original_hash": "f062b3b18449593ef8e0fcc029868781",
"translation_date": "2025-08-29T23:03:28+00:00",
"source_file": "lessons/6-Other/22-DeepRL/lab/MountainCar.ipynb",
"language_code": "bg"
}
},
"nbformat": 4,
"nbformat_minor": 4
}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -1,140 +1,151 @@
<!--
CO_OP_TRANSLATOR_METADATA:
{
"original_hash": "f3a6b0ddf7e6e3f33b2a543baf086dc9",
"translation_date": "2025-08-25T21:11:32+00:00",
"original_hash": "07191303b7ea2aff1d47e2b0fe4bb862",
"translation_date": "2025-08-29T22:47:45+00:00",
"source_file": "README.md",
"language_code": "ro"
}
-->
[![Licență GitHub](https://img.shields.io/github/license/microsoft/AI-For-Beginners.svg)](https://github.com/microsoft/AI-For-Beginners/blob/main/LICENSE)
[![Contribuitori GitHub](https://img.shields.io/github/contributors/microsoft/AI-For-Beginners.svg)](https://GitHub.com/microsoft/AI-For-Beginners/graphs/contributors/)
[![Probleme GitHub](https://img.shields.io/github/issues/microsoft/AI-For-Beginners.svg)](https://GitHub.com/microsoft/AI-For-Beginners/issues/)
[![Pull-requests GitHub](https://img.shields.io/github/issues-pr/microsoft/AI-For-Beginners.svg)](https://GitHub.com/microsoft/AI-For-Beginners/pulls/)
[![GitHub license](https://img.shields.io/github/license/microsoft/AI-For-Beginners.svg)](https://github.com/microsoft/AI-For-Beginners/blob/main/LICENSE)
[![GitHub contributors](https://img.shields.io/github/contributors/microsoft/AI-For-Beginners.svg)](https://GitHub.com/microsoft/AI-For-Beginners/graphs/contributors/)
[![GitHub issues](https://img.shields.io/github/issues/microsoft/AI-For-Beginners.svg)](https://GitHub.com/microsoft/AI-For-Beginners/issues/)
[![GitHub pull-requests](https://img.shields.io/github/issues-pr/microsoft/AI-For-Beginners.svg)](https://GitHub.com/microsoft/AI-For-Beginners/pulls/)
[![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg?style=flat-square)](http://makeapullrequest.com)
[![Observatori GitHub](https://img.shields.io/github/watchers/microsoft/AI-For-Beginners.svg?style=social&label=Watch)](https://GitHub.com/microsoft/AI-For-Beginners/watchers/)
[![Fork-uri GitHub](https://img.shields.io/github/forks/microsoft/AI-For-Beginners.svg?style=social&label=Fork)](https://GitHub.com/microsoft/AI-For-Beginners/network/)
[![Stele GitHub](https://img.shields.io/github/stars/microsoft/AI-For-Beginners.svg?style=social&label=Star)](https://GitHub.com/microsoft/AI-For-Beginners/stargazers/)
[![GitHub watchers](https://img.shields.io/github/watchers/microsoft/AI-For-Beginners.svg?style=social&label=Watch)](https://GitHub.com/microsoft/AI-For-Beginners/watchers/)
[![GitHub forks](https://img.shields.io/github/forks/microsoft/AI-For-Beginners.svg?style=social&label=Fork)](https://GitHub.com/microsoft/AI-For-Beginners/network/)
[![GitHub stars](https://img.shields.io/github/stars/microsoft/AI-For-Beginners.svg?style=social&label=Star)](https://GitHub.com/microsoft/AI-For-Beginners/stargazers/)
[![Binder](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/microsoft/ai-for-beginners/HEAD)
[![Gitter](https://badges.gitter.im/Microsoft/ai-for-beginners.svg)](https://gitter.im/Microsoft/ai-for-beginners?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge)
[![](https://dcbadge.vercel.app/api/server/ByRwuEEgH4)](https://discord.gg/zxKYvhSnVp?WT.mc_id=academic-000002-leestott)
# Inteligență Artificială pentru Începători - Un Curriculum
# Inteligență Artificială pentru Începători - Un Curriculum
|![ Sketchnote de [(@girlie_mac)](https://twitter.com/girlie_mac) ](./lessons/sketchnotes/ai-overview.png)|
|![Sketchnote de @girlie_mac https://twitter.com/girlie_mac](../../translated_images/ai-overview.0857791951d19500d0ef8b803d77110c738dcafc52306e6d68724742cd4af167.ro.png)|
|:---:|
| AI Pentru Începători - _Sketchnote de [@girlie_mac](https://twitter.com/girlie_mac)_ |
Explorează lumea **Inteligenței Artificiale** (IA) cu curriculum-ul nostru de 12 săptămâni, 24 lecții! Include lecții practice, teste și laboratoare. Curriculum-ul este prietenos pentru începători și acoperă instrumente precum TensorFlow și PyTorch, precum și etica în IA.
Explorează lumea **Inteligenței Artificiale** (AI) cu curriculum-ul nostru de 12 săptămâni și 24 de lecții! Acesta include lecții practice, teste și laboratoare. Curriculum-ul este prietenos pentru începători și acoperă instrumente precum TensorFlow și PyTorch, precum și etica în AI.
### 🌐 Suport Multi-Limbă
#### Suportat prin GitHub Action (Automatizat & Mereu Actualizat)
[French](../fr/README.md) | [Spanish](../es/README.md) | [German](../de/README.md) | [Russian](../ru/README.md) | [Arabic](../ar/README.md) | [Persian (Farsi)](../fa/README.md) | [Urdu](../ur/README.md) | [Chinese (Simplified)](../zh/README.md) | [Chinese (Traditional, Macau)](../mo/README.md) | [Chinese (Traditional, Hong Kong)](../hk/README.md) | [Chinese (Traditional, Taiwan)](../tw/README.md) | [Japanese](../ja/README.md) | [Korean](../ko/README.md) | [Hindi](../hi/README.md) | [Bengali](../bn/README.md) | [Marathi](../mr/README.md) | [Nepali](../ne/README.md) | [Punjabi (Gurmukhi)](../pa/README.md) | [Portuguese (Portugal)](../pt/README.md) | [Portuguese (Brazil)](../br/README.md) | [Italian](../it/README.md) | [Polish](../pl/README.md) | [Turkish](../tr/README.md) | [Greek](../el/README.md) | [Thai](../th/README.md) | [Swedish](../sv/README.md) | [Danish](../da/README.md) | [Norwegian](../no/README.md) | [Finnish](../fi/README.md) | [Dutch](../nl/README.md) | [Hebrew](../he/README.md) | [Vietnamese](../vi/README.md) | [Indonesian](../id/README.md) | [Malay](../ms/README.md) | [Tagalog (Filipino)](../tl/README.md) | [Swahili](../sw/README.md) | [Hungarian](../hu/README.md) | [Czech](../cs/README.md) | [Slovak](../sk/README.md) | [Romanian](./README.md) | [Bulgarian](../bg/README.md) | [Serbian (Cyrillic)](../sr/README.md) | [Croatian](../hr/README.md) | [Slovenian](../sl/README.md) | [Ukrainian](../uk/README.md) | [Burmese (Myanmar)](../my/README.md)
**Dacă dorești să fie suportate limbi suplimentare, acestea sunt listate [aici](https://github.com/Azure/co-op-translator/blob/main/getting_started/supported-languages.md)**
## Alătură-te Comunității
[![Azure AI Discord](https://dcbadge.limes.pink/api/server/kzRShWzttr)](https://discord.gg/kzRShWzttr)
## Ce vei învăța
**[Harta mentală a cursului](http://soshnikov.com/courses/ai-for-beginners/mindmap.html)**
**[Mindmap-ul Cursului](http://soshnikov.com/courses/ai-for-beginners/mindmap.html)**
În acest curriculum, vei învăța:
* Diferite abordări ale Inteligenței Artificiale, inclusiv abordarea simbolică „bună și veche” cu **Reprezentarea Cunoștințelor** și raționamentul ([GOFAI](https://en.wikipedia.org/wiki/Symbolic_artificial_intelligence)).
* **Rețele Neurale** și **Învățare Profundă**, care sunt în centrul IA modernă. Vom ilustra conceptele din spatele acestor subiecte importante folosind cod în două dintre cele mai populare cadre - [TensorFlow](http://Tensorflow.org) și [PyTorch](http://pytorch.org).
* Diferite abordări ale Inteligenței Artificiale, inclusiv abordarea simbolică "bună și veche" cu **Reprezentarea Cunoștințelor** și raționamentul ([GOFAI](https://en.wikipedia.org/wiki/Symbolic_artificial_intelligence)).
* **Rețele Neurale** și **Învățare Profundă**, care sunt în centrul AI-ului modern. Vom ilustra conceptele din spatele acestor subiecte importante folosind cod în două dintre cele mai populare framework-uri - [TensorFlow](http://Tensorflow.org) și [PyTorch](http://pytorch.org).
* **Arhitecturi Neurale** pentru lucrul cu imagini și text. Vom acoperi modele recente, dar este posibil să nu includem cele mai avansate.
* Abordări mai puțin populare ale IA, cum ar fi **Algoritmi Genetici** și **Sisteme Multi-Agent**.
* Abordări mai puțin populare ale AI, cum ar fi **Algoritmi Genetici** și **Sisteme Multi-Agent**.
Ce nu vom acoperi în acest curriculum:
> [Găsește toate resursele suplimentare pentru acest curs în colecția noastră Microsoft Learn](https://learn.microsoft.com/en-us/collections/7w28iy2xrqzdj0?WT.mc_id=academic-77998-bethanycheum)
* Cazuri de utilizare a **IA în Afaceri**. Ia în considerare parcurgerea traseului de învățare [Introducere în IA pentru utilizatorii de afaceri](https://docs.microsoft.com/learn/paths/introduction-ai-for-business-users/?WT.mc_id=academic-77998-bethanycheum) pe Microsoft Learn sau [Școala de Afaceri IA](https://www.microsoft.com/ai/ai-business-school/?WT.mc_id=academic-77998-bethanycheum), dezvoltată în colaborare cu [INSEAD](https://www.insead.edu/).
* Cazuri de utilizare ale **AI în Afaceri**. Ia în considerare parcurgerea [Introducere în AI pentru utilizatorii de afaceri](https://docs.microsoft.com/learn/paths/introduction-ai-for-business-users/?WT.mc_id=academic-77998-bethanycheum) pe Microsoft Learn sau [AI Business School](https://www.microsoft.com/ai/ai-business-school/?WT.mc_id=academic-77998-bethanycheum), dezvoltat în colaborare cu [INSEAD](https://www.insead.edu/).
* **Învățare Automată Clasică**, care este bine descrisă în [Curriculum-ul nostru de Învățare Automată pentru Începători](http://github.com/Microsoft/ML-for-Beginners).
* Aplicații practice IA construite folosind **[Servicii Cognitive](https://azure.microsoft.com/services/cognitive-services/?WT.mc_id=academic-77998-bethanycheum)**. Pentru aceasta, îți recomandăm să începi cu modulele Microsoft Learn pentru [viziune](https://docs.microsoft.com/learn/paths/create-computer-vision-solutions-azure-cognitive-services/?WT.mc_id=academic-77998-bethanycheum), [procesarea limbajului natural](https://docs.microsoft.com/learn/paths/explore-natural-language-processing/?WT.mc_id=academic-77998-bethanycheum), **[Generative AI cu Azure OpenAI Service](https://learn.microsoft.com/en-us/training/paths/develop-ai-solutions-azure-openai/?WT.mc_id=academic-77998-bethanycheum)** și altele.
* Cadre specifice ML **Cloud**, cum ar fi [Azure Machine Learning](https://azure.microsoft.com/services/machine-learning/?WT.mc_id=academic-77998-bethanycheum), [Microsoft Fabric](https://learn.microsoft.com/en-us/training/paths/get-started-fabric/?WT.mc_id=academic-77998-bethanycheum) sau [Azure Databricks](https://docs.microsoft.com/learn/paths/data-engineer-azure-databricks?WT.mc_id=academic-77998-bethanycheum). Ia în considerare utilizarea traseelor de învățare [Construiește și operează soluții de învățare automată cu Azure Machine Learning](https://docs.microsoft.com/learn/paths/build-ai-solutions-with-azure-ml-service/?WT.mc_id=academic-77998-bethanycheum) și [Construiește și operează soluții de învățare automată cu Azure Databricks](https://docs.microsoft.com/learn/paths/build-operate-machine-learning-solutions-azure-databricks/?WT.mc_id=academic-77998-bethanycheum).
* **IA Conversațională** și **Chat Bots**. Există un traseu de învățare separat [Creează soluții IA conversaționale](https://docs.microsoft.com/learn/paths/create-conversational-ai-solutions/?WT.mc_id=academic-77998-bethanycheum), și poți consulta și [această postare pe blog](https://soshnikov.com/azure/hello-bot-conversational-ai-on-microsoft-platform/) pentru mai multe detalii.
* **Matematica profundă** din spatele învățării profunde. Pentru aceasta, îți recomandăm [Deep Learning](https://www.amazon.com/Deep-Learning-Adaptive-Computation-Machine/dp/0262035618) de Ian Goodfellow, Yoshua Bengio și Aaron Courville, care este disponibilă online la [https://www.deeplearningbook.org/](https://www.deeplearningbook.org/).
* Aplicații practice AI construite folosind **[Servicii Cognitive](https://azure.microsoft.com/services/cognitive-services/?WT.mc_id=academic-77998-bethanycheum)**. Pentru aceasta, recomandăm să începi cu modulele Microsoft Learn pentru [viziune](https://docs.microsoft.com/learn/paths/create-computer-vision-solutions-azure-cognitive-services/?WT.mc_id=academic-77998-bethanycheum), [procesarea limbajului natural](https://docs.microsoft.com/learn/paths/explore-natural-language-processing/?WT.mc_id=academic-77998-bethanycheum), **[Generative AI cu Azure OpenAI Service](https://learn.microsoft.com/en-us/training/paths/develop-ai-solutions-azure-openai/?WT.mc_id=academic-77998-bethanycheum)** și altele.
* Framework-uri specifice ML **Cloud**, cum ar fi [Azure Machine Learning](https://azure.microsoft.com/services/machine-learning/?WT.mc_id=academic-77998-bethanycheum), [Microsoft Fabric](https://learn.microsoft.com/en-us/training/paths/get-started-fabric/?WT.mc_id=academic-77998-bethanycheum) sau [Azure Databricks](https://docs.microsoft.com/learn/paths/data-engineer-azure-databricks?WT.mc_id=academic-77998-bethanycheum). Ia în considerare utilizarea [Construiește și operează soluții de învățare automată cu Azure Machine Learning](https://docs.microsoft.com/learn/paths/build-ai-solutions-with-azure-ml-service/?WT.mc_id=academic-77998-bethanycheum) și [Construiește și operează soluții de învățare automată cu Azure Databricks](https://docs.microsoft.com/learn/paths/build-operate-machine-learning-solutions-azure-databricks/?WT.mc_id=academic-77998-bethanycheum).
* **AI Conversațională** și **Chat Bots**. Există un [Creează soluții AI conversaționale](https://docs.microsoft.com/learn/paths/create-conversational-ai-solutions/?WT.mc_id=academic-77998-bethanycheum) learning path separat, și poți consulta [acest articol de blog](https://soshnikov.com/azure/hello-bot-conversational-ai-on-microsoft-platform/) pentru mai multe detalii.
* **Matematica profundă** din spatele învățării profunde. Pentru aceasta, recomandăm [Deep Learning](https://www.amazon.com/Deep-Learning-Adaptive-Computation-Machine/dp/0262035618) de Ian Goodfellow, Yoshua Bengio și Aaron Courville, care este disponibilă online la [https://www.deeplearningbook.org/](https://www.deeplearningbook.org/).
Pentru o introducere ușoară în subiectele _IA în Cloud_, poți lua în considerare parcurgerea traseului de învățare [Începe cu inteligența artificială pe Azure](https://docs.microsoft.com/learn/paths/get-started-with-artificial-intelligence-on-azure/?WT.mc_id=academic-77998-bethanycheum).
Pentru o introducere ușoară în subiectele _AI în Cloud_, poți lua în considerare parcurgerea [Începe cu inteligența artificială pe Azure](https://docs.microsoft.com/learn/paths/get-started-with-artificial-intelligence-on-azure/?WT.mc_id=academic-77998-bethanycheum) Learning Path.
# Conținut
| | Link Lecție | PyTorch/Keras/TensorFlow | Lab |
| :-: | :------------------------------------------------------------------------------------------------------------------------------------------: | :---------------------------------------------------------------------------------------------: | ------------------------------------------------------------------------------ |
| 0 | [Configurarea Cursului](./lessons/0-course-setup/setup.md) | [Configurează Mediul de Dezvoltare](./lessons/0-course-setup/how-to-run.md) | |
| I | [**Introducere în IA**](./lessons/1-Intro/README.md) | | |
| 01 | [Introducere și Istoria IA](./lessons/1-Intro/README.md) | - | - |
| II | **IA Simbolică** |
| 02 | [Reprezentarea Cunoștințelor și Sisteme Expert](./lessons/2-Symbolic/README.md) | [Sisteme Expert](https://github.com/microsoft/AI-For-Beginners/blob/main/lessons/2-Symbolic/Animals.ipynb) / [Ontologie](https://github.com/microsoft/AI-For-Beginners/blob/main/lessons/2-Symbolic/FamilyOntology.ipynb) /[Grafic de Concepte](https://github.com/microsoft/AI-For-Beginners/blob/main/lessons/2-Symbolic/MSConceptGraph.ipynb) | |
| 0 | [Setarea Cursului](./lessons/0-course-setup/setup.md) | [Setează Mediul de Dezvoltare](./lessons/0-course-setup/how-to-run.md) | |
| I | [**Introducere în AI**](./lessons/1-Intro/README.md) | | |
| 01 | [Introducere și Istoria AI](./lessons/1-Intro/README.md) | - | - |
| II | **AI Simbolică** |
| 02 | [Reprezentarea Cunoștințelor și Sisteme Expert](./lessons/2-Symbolic/README.md) | [Sisteme Expert](./lessons/2-Symbolic/Animals.ipynb) / [Ontologie](./lessons/2-Symbolic/FamilyOntology.ipynb) /[Grafic Conceptual](./lessons/2-Symbolic/MSConceptGraph.ipynb) | |
| III | [**Introducere în Rețele Neurale**](./lessons/3-NeuralNetworks/README.md) |||
| 03 | [Perceptron](./lessons/3-NeuralNetworks/03-Perceptron/README.md) | [Notebook](https://github.com/microsoft/AI-For-Beginners/blob/main/lessons/3-NeuralNetworks/03-Perceptron/Perceptron.ipynb) | [Lab](./lessons/3-NeuralNetworks/03-Perceptron/lab/README.md) |
| 04 | [Perceptron Multi-Strat și Crearea propriului Cadru](./lessons/3-NeuralNetworks/04-OwnFramework/README.md) | [Notebook](https://github.com/microsoft/AI-For-Beginners/blob/main/lessons/3-NeuralNetworks/04-OwnFramework/OwnFramework.ipynb) | [Lab](./lessons/3-NeuralNetworks/04-OwnFramework/lab/README.md) |
| 05 | [Introducere în Cadre (PyTorch/TensorFlow) și Supraînvățare](./lessons/3-NeuralNetworks/05-Frameworks/README.md) | [PyTorch](https://github.com/microsoft/AI-For-Beginners/blob/main/lessons/3-NeuralNetworks/05-Frameworks/IntroPyTorch.ipynb) / [Keras](https://github.com/microsoft/AI-For-Beginners/blob/main/lessons/3-NeuralNetworks/05-Frameworks/IntroKeras.ipynb) / [TensorFlow](https://github.com/microsoft/AI-For-Beginners/blob/main/lessons/3-NeuralNetworks/05-Frameworks/IntroKerasTF.ipynb) | [Lab](./lessons/3-NeuralNetworks/05-Frameworks/lab/README.md) |
| IV | [**Viziune Computațională**](./lessons/4-ComputerVision/README.md) | [PyTorch](https://docs.microsoft.com/learn/modules/intro-computer-vision-pytorch/?WT.mc_id=academic-77998-cacaste) / [TensorFlow](https://docs.microsoft.com/learn/modules/intro-computer-vision-TensorFlow/?WT.mc_id=academic-77998-cacaste)| [Explorează Viziunea Computațională pe Microsoft Azure](https://learn.microsoft.com/en-us/collections/7w28iy2xrqzdj0?WT.mc_id=academic-77998-bethanycheum) |
| 06 | [Introducere în Viziunea Computațională. OpenCV](./lessons/4-ComputerVision/06-IntroCV/README.md) | [Notebook](https://github.com/microsoft/AI-For-Beginners/blob/main/lessons/4-ComputerVision/06-IntroCV/OpenCV.ipynb) | [Lab](./lessons/4-ComputerVision/06-IntroCV/lab/README.md) |
| 07 | [Rețele Neurale Convoluționale](./lessons/4-ComputerVision/07-ConvNets/README.md) & [Arhitecturi CNN](./lessons/4-ComputerVision/07-ConvNets/CNN_Architectures.md) | [PyTorch](https://github.com/microsoft/AI-For-Beginners/blob/main/lessons/4-ComputerVision/07-ConvNets/ConvNetsPyTorch.ipynb) /[TensorFlow](https://microsoft.github.io/AI-For-Beginners/lessons/4-ComputerVision/07-ConvNets/ConvNetsTF.ipynb) | [Lab](./lessons/4-ComputerVision/07-ConvNets/lab/README.md) |
| 08 | [Rețele pre-antrenate și Transfer Learning](./lessons/4-ComputerVision/08-TransferLearning/README.md) și [Trucuri pentru antrenare](./lessons/4-ComputerVision/08-TransferLearning/TrainingTricks.md) | [PyTorch](https://github.com/microsoft/AI-For-Beginners/blob/main/lessons/4-ComputerVision/08-TransferLearning/TransferLearningPyTorch.ipynb) / [TensorFlow](https://github.com/microsoft/AI-For-Beginners/blob/main/lessons/3-NeuralNetworks/05-Frameworks/IntroKerasTF.ipynb) | [Laborator](./lessons/4-ComputerVision/08-TransferLearning/lab/README.md) |
| 09 | [Autoencodere și VAEs](./lessons/4-ComputerVision/09-Autoencoders/README.md) | [PyTorch](https://github.com/microsoft/AI-For-Beginners/blob/main/lessons/4-ComputerVision/09-Autoencoders/AutoEncodersPyTorch.ipynb) / [TensorFlow](https://github.com/microsoft/AI-For-Beginners/blob/main/lessons/4-ComputerVision/09-Autoencoders/AutoencodersTF.ipynb) | |
| 10 | [Rețele Generative Adversariale & Transfer de Stil Artistic](./lessons/4-ComputerVision/10-GANs/README.md) | [PyTorch](https://github.com/microsoft/AI-For-Beginners/blob/main/lessons/4-ComputerVision/10-GANs/GANPyTorch.ipynb) / [TensorFlow](https://github.com/microsoft/AI-For-Beginners/blob/main/lessons/4-ComputerVision/10-GANs/GANTF.ipynb) | |
| 11 | [Detecția Obiectelor](./lessons/4-ComputerVision/11-ObjectDetection/README.md) | [TensorFlow](https://github.com/microsoft/AI-For-Beginners/blob/main/lessons/4-ComputerVision/11-ObjectDetection/ObjectDetection.ipynb) | [Laborator](./lessons/4-ComputerVision/11-ObjectDetection/lab/README.md) |
| 12 | [Segmentare Semantică. U-Net](./lessons/4-ComputerVision/12-Segmentation/README.md) | [PyTorch](https://github.com/microsoft/AI-For-Beginners/blob/main/lessons/4-ComputerVision/12-Segmentation/SemanticSegmentationPytorch.ipynb) / [TensorFlow](../../(https:/github.com/microsoft/AI-For-Beginners/blob/main/lessons/4-ComputerVision/12-Segmentation/SemanticSegmentationTF.ipynb)) | |
| 03 | [Perceptron](./lessons/3-NeuralNetworks/03-Perceptron/README.md) | [Notebook](./lessons/3-NeuralNetworks/03-Perceptron/Perceptron.ipynb) | [Lab](./lessons/3-NeuralNetworks/03-Perceptron/lab/README.md) |
| 04 | [Perceptron Multi-Strat și Crearea propriului Framework](./lessons/3-NeuralNetworks/04-OwnFramework/README.md) | [Notebook](./lessons/3-NeuralNetworks/04-OwnFramework/OwnFramework.ipynb) | [Lab](./lessons/3-NeuralNetworks/04-OwnFramework/lab/README.md) |
| 05 | [Introducere în Framework-uri (PyTorch/TensorFlow) și Overfitting](./lessons/3-NeuralNetworks/05-Frameworks/README.md) | [PyTorch](./lessons/3-NeuralNetworks/05-Frameworks/IntroPyTorch.ipynb) / [Keras](./lessons/3-NeuralNetworks/05-Frameworks/IntroKeras.ipynb) / [TensorFlow](./lessons/3-NeuralNetworks/05-Frameworks/IntroKerasTF.ipynb) | [Laborator](./lessons/3-NeuralNetworks/05-Frameworks/lab/README.md) |
| IV | [**Viziune Computațională**](./lessons/4-ComputerVision/README.md) | [PyTorch](https://docs.microsoft.com/learn/modules/intro-computer-vision-pytorch/?WT.mc_id=academic-77998-cacaste) / [TensorFlow](https://docs.microsoft.com/learn/modules/intro-computer-vision-TensorFlow/?WT.mc_id=academic-77998-cacaste)| [Explorează Viziunea Computațională pe Microsoft Azure](https://learn.microsoft.com/en-us/collections/7w28iy2xrqzdj0?WT.mc_id=academic-77998-bethanycheum) |
| 06 | [Introducere în Viziunea Computațională. OpenCV](./lessons/4-ComputerVision/06-IntroCV/README.md) | [Notebook](./lessons/4-ComputerVision/06-IntroCV/OpenCV.ipynb) | [Laborator](./lessons/4-ComputerVision/06-IntroCV/lab/README.md) |
| 07 | [Rețele Neuronale Convoluționale](./lessons/4-ComputerVision/07-ConvNets/README.md) & [Arhitecturi CNN](./lessons/4-ComputerVision/07-ConvNets/CNN_Architectures.md) | [PyTorch](./lessons/4-ComputerVision/07-ConvNets/ConvNetsPyTorch.ipynb) /[TensorFlow](./lessons/4-ComputerVision/07-ConvNets/ConvNetsTF.ipynb) | [Laborator](./lessons/4-ComputerVision/07-ConvNets/lab/README.md) |
| 08 | [Rețele Pre-antrenate și Transfer Learning](./lessons/4-ComputerVision/08-TransferLearning/README.md) și [Trucuri de Antrenament](./lessons/4-ComputerVision/08-TransferLearning/TrainingTricks.md) | [PyTorch](./lessons/4-ComputerVision/08-TransferLearning/TransferLearningPyTorch.ipynb) / [TensorFlow](./lessons/3-NeuralNetworks/05-Frameworks/IntroKerasTF.ipynb) | [Laborator](./lessons/4-ComputerVision/08-TransferLearning/lab/README.md) |
| 09 | [Autoencodere și VAEs](./lessons/4-ComputerVision/09-Autoencoders/README.md) | [PyTorch](./lessons/4-ComputerVision/09-Autoencoders/AutoEncodersPyTorch.ipynb) / [TensorFlow](./lessons/4-ComputerVision/09-Autoencoders/AutoencodersTF.ipynb) | |
| 10 | [Rețele Generative Adversariale & Transfer Artistic de Stil](./lessons/4-ComputerVision/10-GANs/README.md) | [PyTorch](./lessons/4-ComputerVision/10-GANs/GANPyTorch.ipynb) / [TensorFlow](./lessons/4-ComputerVision/10-GANs/GANTF.ipynb) | |
| 11 | [Detectarea Obiectelor](./lessons/4-ComputerVision/11-ObjectDetection/README.md) | [TensorFlow](./lessons/4-ComputerVision/11-ObjectDetection/ObjectDetection.ipynb) | [Laborator](./lessons/4-ComputerVision/11-ObjectDetection/lab/README.md) |
| 12 | [Segmentare Semantică. U-Net](./lessons/4-ComputerVision/12-Segmentation/README.md) | [PyTorch](./lessons/4-ComputerVision/12-Segmentation/SemanticSegmentationPytorch.ipynb) / [TensorFlow](./lessons/4-ComputerVision/12-Segmentation/SemanticSegmentationTF.ipynb) | |
| V | [**Procesarea Limbajului Natural**](./lessons/5-NLP/README.md) | [PyTorch](https://docs.microsoft.com/learn/modules/intro-natural-language-processing-pytorch/?WT.mc_id=academic-77998-cacaste) /[TensorFlow](https://docs.microsoft.com/learn/modules/intro-natural-language-processing-TensorFlow/?WT.mc_id=academic-77998-cacaste) | [Explorează Procesarea Limbajului Natural pe Microsoft Azure](https://learn.microsoft.com/en-us/collections/7w28iy2xrqzdj0?WT.mc_id=academic-77998-bethanycheum)|
| 13 | [Reprezentarea Textului. Bow/TF-IDF](./lessons/5-NLP/13-TextRep/README.md) | [PyTorch](https://github.com/microsoft/AI-For-Beginners/blob/main/lessons/5-NLP/13-TextRep/TextRepresentationPyTorch.ipynb) / [TensorFlow](https://github.com/microsoft/AI-For-Beginners/blob/main/lessons/5-NLP/13-TextRep/TextRepresentationTF.ipynb) | |
| 14 | [Embedări semantice ale cuvintelor. Word2Vec și GloVe](./lessons/5-NLP/14-Embeddings/README.md) | [PyTorch](https://github.com/microsoft/AI-For-Beginners/blob/main/lessons/5-NLP/14-Embeddings/EmbeddingsPyTorch.ipynb) / [TensorFlow](https://github.com/microsoft/AI-For-Beginners/blob/main/lessons/5-NLP/14-Embeddings/EmbeddingsTF.ipynb) | |
| 15 | [Modelarea Limbajului. Antrenarea propriilor embedări](./lessons/5-NLP/15-LanguageModeling/README.md) | [PyTorch](https://github.com/microsoft/AI-For-Beginners/blob/main/lessons/5-NLP/15-LanguageModeling/CBoW-PyTorch.ipynb) / [TensorFlow](https://github.com/microsoft/AI-For-Beginners/blob/main/lessons/5-NLP/15-LanguageModeling/CBoW-TF.ipynb) | [Laborator](./lessons/5-NLP/15-LanguageModeling/lab/README.md) |
| 16 | [Rețele Neuronale Recurente](./lessons/5-NLP/16-RNN/README.md) | [PyTorch](https://github.com/microsoft/AI-For-Beginners/blob/main/lessons/5-NLP/16-RNN/RNNPyTorch.ipynb) / [TensorFlow](https://github.com/microsoft/AI-For-Beginners/blob/main/lessons/5-NLP/16-RNN/RNNTF.ipynb) | |
| 17 | [Rețele Generative Recurente](./lessons/5-NLP/17-GenerativeNetworks/README.md) | [PyTorch](https://microsoft.github.io/AI-For-Beginners/lessons/5-NLP/17-GenerativeNetworks/GenerativePyTorch.md) / [TensorFlow](https://microsoft.github.io/AI-For-Beginners/lessons/5-NLP/17-GenerativeNetworks/GenerativeTF.md) | [Laborator](./lessons/5-NLP/17-GenerativeNetworks/lab/README.md) |
| 18 | [Transformers. BERT.](./lessons/5-NLP/18-Transformers/READMEtransformers.md) | [PyTorch](https://github.com/microsoft/AI-For-Beginners/blob/main/lessons/5-NLP/18-Transformers/TransformersPyTorch.ipynb) /[TensorFlow](https://github.com/microsoft/AI-For-Beginners/blob/main/lessons/5-NLP/18-Transformers/TransformersTF.ipynb) | |
| 19 | [Recunoașterea Entităților Numite](./lessons/5-NLP/19-NER/README.md) | [TensorFlow](https://microsoft.github.io/AI-For-Beginners/lessons/5-NLP/19-NER/NER-TF.ipynb) | [Laborator](./lessons/5-NLP/19-NER/lab/README.md) |
| 20 | [Modele Mari de Limbaj, Programarea Prompturilor și Sarcini Few-Shot](./lessons/5-NLP/20-LangModels/READMELargeLang.md) | [PyTorch](https://microsoft.github.io/AI-For-Beginners/lessons/5-NLP/20-LangModels/GPT-PyTorch.ipynb) | |
| 13 | [Reprezentarea Textului. Bow/TF-IDF](./lessons/5-NLP/13-TextRep/README.md) | [PyTorch](./lessons/5-NLP/13-TextRep/TextRepresentationPyTorch.ipynb) / [TensorFlow](./lessons/5-NLP/13-TextRep/TextRepresentationTF.ipynb) | |
| 14 | [Embedări Semantice ale Cuvintelor. Word2Vec și GloVe](./lessons/5-NLP/14-Embeddings/README.md) | [PyTorch](./lessons/5-NLP/14-Embeddings/EmbeddingsPyTorch.ipynb) / [TensorFlow](./lessons/5-NLP/14-Embeddings/EmbeddingsTF.ipynb) | |
| 15 | [Modelarea Limbajului. Antrenarea propriilor embedări](./lessons/5-NLP/15-LanguageModeling/README.md) | [PyTorch](./lessons/5-NLP/15-LanguageModeling/CBoW-PyTorch.ipynb) / [TensorFlow](./lessons/5-NLP/15-LanguageModeling/CBoW-TF.ipynb) | [Laborator](./lessons/5-NLP/15-LanguageModeling/lab/README.md) |
| 16 | [Rețele Neuronale Recurente](./lessons/5-NLP/16-RNN/README.md) | [PyTorch](./lessons/5-NLP/16-RNN/RNNPyTorch.ipynb) / [TensorFlow](./lessons/5-NLP/16-RNN/RNNTF.ipynb) | |
| 17 | [Rețele Recurente Generative](./lessons/5-NLP/17-GenerativeNetworks/README.md) | [PyTorch](./lessons/5-NLP/17-GenerativeNetworks/GenerativePyTorch.md) / [TensorFlow](./lessons/5-NLP/17-GenerativeNetworks/GenerativeTF.md) | [Laborator](./lessons/5-NLP/17-GenerativeNetworks/lab/README.md) |
| 18 | [Transformers. BERT.](./lessons/5-NLP/18-Transformers/READMEtransformers.md) | [PyTorch](./lessons/5-NLP/18-Transformers/TransformersPyTorch.ipynb) /[TensorFlow](./lessons/5-NLP/18-Transformers/TransformersTF.ipynb) | |
| 19 | [Recunoașterea Entităților Denumite](./lessons/5-NLP/19-NER/README.md) | [TensorFlow](./lessons/5-NLP/19-NER/NER-TF.ipynb) | [Laborator](./lessons/5-NLP/19-NER/lab/README.md) |
| 20 | [Modele de Limbaj Mari, Programarea Prompturilor și Sarcini Few-Shot](./lessons/5-NLP/20-LangModels/READMELargeLang.md) | [PyTorch](./lessons/5-NLP/20-LangModels/GPT-PyTorch.ipynb) | |
| VI | **Alte Tehnici AI** || |
| 21 | [Algoritmi Genetici](./lessons/6-Other/21-GeneticAlgorithms/README.md) | [Notebook](https://github.com/microsoft/AI-For-Beginners/blob/main/lessons/6-Other/21-GeneticAlgorithms/Genetic.ipynb) | |
| 22 | [Învățare prin Recompensă Profundă](./lessons/6-Other/22-DeepRL/README.md) | [PyTorch](https://github.com/microsoft/AI-For-Beginners/blob/main/lessons/6-Other/22-DeepRL/CartPole-RL-PyTorch.ipynb) /[TensorFlow](https://github.com/microsoft/AI-For-Beginners/blob/main/lessons/6-Other/22-DeepRL/CartPole-RL-TF.ipynb) | [Laborator](./lessons/6-Other/22-DeepRL/lab/README.md) |
| 21 | [Algoritmi Genetici](./lessons/6-Other/21-GeneticAlgorithms/README.md) | [Notebook](./lessons/6-Other/21-GeneticAlgorithms/Genetic.ipynb) | |
| 22 | [Învățare prin Recompensă Profundă](./lessons/6-Other/22-DeepRL/README.md) | [PyTorch](./lessons/6-Other/22-DeepRL/CartPole-RL-PyTorch.ipynb) /[TensorFlow](./lessons/6-Other/22-DeepRL/CartPole-RL-TF.ipynb) | [Laborator](./lessons/6-Other/22-DeepRL/lab/README.md) |
| 23 | [Sisteme Multi-Agent](./lessons/6-Other/23-MultiagentSystems/README.md) | | |
| VII | **Etica AI** | | |
| 24 | [Etica AI și AI Responsabil](./lessons/7-Ethics/README.md) | [Microsoft Learn: Principii AI Responsabil](https://docs.microsoft.com/learn/paths/responsible-ai-business-principles/?WT.mc_id=academic-77998-cacaste) | |
| IX | **Extra** | | |
| 25 | [Rețele Multi-Modale, CLIP și VQGAN](./lessons/X-Extras/X1-MultiModal/README.md) | [Notebook](https://github.com/microsoft/AI-For-Beginners/blob/main/lessons/X-Extras/X1-MultiModal/Clip.ipynb) | |
| 25 | [Rețele Multi-Modale, CLIP și VQGAN](./lessons/X-Extras/X1-MultiModal/README.md) | [Notebook](./lessons/X-Extras/X1-MultiModal/Clip.ipynb) | |
## Fiecare lecție conține
* Material de citit înainte
* Jupyter Notebooks executabile, care sunt adesea specifice unui framework (**PyTorch** sau **TensorFlow**). Notebook-ul executabil conține, de asemenea, mult material teoretic, așa că pentru a înțelege subiectul trebuie să parcurgi cel puțin o versiune a notebook-ului (fie PyTorch, fie TensorFlow).
* Material de pre-citire
* Jupyter Notebooks executabile, care sunt adesea specifice framework-ului (**PyTorch** sau **TensorFlow**). Notebook-ul executabil conține, de asemenea, mult material teoretic, așa că pentru a înțelege subiectul trebuie să parcurgi cel puțin o versiune a notebook-ului (PyTorch sau TensorFlow).
* **Laboratoare** disponibile pentru unele subiecte, care îți oferă oportunitatea de a aplica materialul învățat la o problemă specifică.
* Unele secțiuni conțin linkuri către module [**MS Learn**](https://learn.microsoft.com/en-us/collections/7w28iy2xrqzdj0?WT.mc_id=academic-77998-bethanycheum) care acoperă subiecte conexe.
* Unele secțiuni conțin linkuri către modulele [**MS Learn**](https://learn.microsoft.com/en-us/collections/7w28iy2xrqzdj0?WT.mc_id=academic-77998-bethanycheum) care acoperă subiecte conexe.
## Începe
- Am creat o [lecție de configurare](https://github.com/microsoft/AI-For-Beginners/blob/main/lessons/0-course-setup/setup.md) pentru a te ajuta să îți configurezi mediul de dezvoltare. - Pentru educatori, am creat și o [lecție de configurare a curriculei](https://github.com/microsoft/AI-For-Beginners/blob/main/lessons/0-course-setup/for-teachers.md)!
- Cum să [rulezi codul în VSCode sau Codepace](https://github.com/microsoft/AI-For-Beginners/blob/main/lessons/0-course-setup/how-to-run.md)
- Am creat o [lecție de configurare](./lessons/0-course-setup/setup.md) pentru a te ajuta să îți configurezi mediul de dezvoltare. - Pentru educatori, am creat și o [lecție de configurare a curriculei](./lessons/0-course-setup/for-teachers.md)!
- Cum să [Rulezi codul în VSCode sau Codepace](./lessons/0-course-setup/how-to-run.md)
Urmează acești pași:
Forkează Repozitoriul: Apasă pe butonul "Fork" din colțul din dreapta sus al acestei pagini.
Fork Repository-ul: Apasă pe butonul "Fork" din colțul dreapta-sus al acestei pagini.
Clonează Repozitoriul: `git clone https://github.com/microsoft/AI-For-Beginners.git`
Clonează Repository-ul: `git clone https://github.com/microsoft/AI-For-Beginners.git`
Nu uita să dai stea (🌟) acestui repo pentru a-l găsi mai ușor mai târziu.
## Cunoaște alți cursanți
Alătură-te [serverului oficial AI Discord](https://aka.ms/genai-discord?WT.mc_id=academic-105485-bethanycheum) pentru a cunoaște și a face networking cu alți cursanți care urmează acest curs și pentru a primi suport.
Alătură-te [serverului oficial AI Discord](https://aka.ms/genai-discord?WT.mc_id=academic-105485-bethanycheum) pentru a întâlni și a face networking cu alți cursanți care urmează acest curs și pentru a primi suport.
Dacă ai feedback despre produs sau întrebări în timp ce construiești, vizitează [Forum-ul Dezvoltatorilor Azure AI Foundry](https://aka.ms/foundry/forum)
## Chestionare
> **O notă despre chestionare**: Toate chestionarele se află în folderul Quiz-app din etc\quiz-app. Acestea sunt legate din cadrul lecțiilor, iar aplicația de chestionare poate fi rulată local sau implementată pe Azure; urmați instrucțiunile din folderul `quiz-app`. Ele sunt localizate treptat.
## Quiz-uri
> **O notă despre chestionare**: Toate chestionarele se află în folderul Quiz-app din etc\quiz-app sau [Online Aici](https://ff-quizzes.netlify.app/). Ele sunt legate din cadrul lecțiilor, iar aplicația de chestionare poate fi rulată local sau implementată pe Azure; urmați instrucțiunile din folderul `quiz-app`. Ele sunt localizate treptat.
## Ajutor necesar
Ai sugestii sau ai găsit greșeli de ortografie sau cod? Deschide o problemă sau creează un pull request.
Ai sugestii sau ai găsit greșeli de ortografie sau cod? Deschide un issue sau creează un pull request.
## Mulțumiri speciale
* **✍️ Autor principal:** [Dmitry Soshnikov](http://soshnikov.com), PhD
* **🔥 Editor:** [Jen Looper](https://twitter.com/jenlooper), PhD
* **✍️ Autor principal:** [Dmitry Soshnikov](http://soshnikov.com), doctor în științe
* **🔥 Editor:** [Jen Looper](https://twitter.com/jenlooper), doctor în științe
* **🎨 Ilustrator sketchnote:** [Tomomi Imura](https://twitter.com/girlie_mac)
* **✅ Creator quiz:** [Lateefah Bello](https://github.com/CinnamonXI), [MLSA](https://studentambassadors.microsoft.com/)
* **🙏 Contribuitori principali:** [Evgenii Pishchik](https://github.com/Pe4enIks)
* **🙏 Contributori principali:** [Evgenii Pishchik](https://github.com/Pe4enIks)
## Alte programe educaționale
## Alte cursuri
Echipa noastră produce și alte programe educaționale! Verifică:
Echipa noastră produce și alte cursuri! Verifică:
- [Generative AI for Beginners](https://aka.ms/genai-beginners)
- [Generative AI for Beginners .NET](https://github.com/microsoft/Generative-AI-for-beginners-dotnet)
@ -151,5 +162,7 @@ Echipa noastră produce și alte programe educaționale! Verifică:
- [Mastering GitHub Copilot for C#/.NET Developers](https://github.com/microsoft/mastering-github-copilot-for-dotnet-csharp-developers)
- [Choose Your Own Copilot Adventure](https://github.com/microsoft/CopilotAdventures)
---
**Declinare de responsabilitate**:
Acest document a fost tradus folosind serviciul de traducere AI [Co-op Translator](https://github.com/Azure/co-op-translator). Deși ne străduim să asigurăm acuratețea, vă rugăm să fiți conștienți că traducerile automate pot conține erori sau inexactități. Documentul original în limba sa natală ar trebui considerat sursa autoritară. Pentru informații critice, se recomandă traducerea profesională realizată de un specialist uman. Nu ne asumăm responsabilitatea pentru eventualele neînțelegeri sau interpretări greșite care pot apărea din utilizarea acestei traduceri.

View File

@ -0,0 +1,478 @@
{
"cells": [
{
"cell_type": "markdown",
"metadata": {
"collapsed": true
},
"source": [
"# Implementarea unui Sistem Expert pentru Animale\n",
"\n",
"Un exemplu din [AI for Beginners Curriculum](http://github.com/microsoft/ai-for-beginners).\n",
"\n",
"În acest exemplu, vom implementa un sistem simplu bazat pe cunoștințe pentru a determina un animal pe baza unor caracteristici fizice. Sistemul poate fi reprezentat prin următorul arbore AND-OR (acesta este doar o parte din arborele complet, putem adăuga cu ușurință mai multe reguli):\n",
"\n",
"![](../../../../translated_images/AND-OR-Tree.5592d2c70187f283703c8e9c0d69d6a786eb370f4ace67f9a7aae5ada3d260b0.ro.png)\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Propriul nostru shell pentru sisteme expert cu inferență inversă\n",
"\n",
"Să încercăm să definim un limbaj simplu pentru reprezentarea cunoștințelor bazat pe reguli de producție. Vom folosi clase Python ca cuvinte cheie pentru a defini regulile. Vor exista, în esență, 3 tipuri de clase:\n",
"* `Ask` reprezintă o întrebare care trebuie adresată utilizatorului. Conține setul de răspunsuri posibile.\n",
"* `If` reprezintă o regulă și este doar o sintaxă simplificată pentru a stoca conținutul regulii.\n",
"* `AND`/`OR` sunt clase care reprezintă ramuri AND/OR ale arborelui. Ele doar stochează lista de argumente în interior. Pentru a simplifica codul, toată funcționalitatea este definită în clasa părinte `Content`.\n"
]
},
{
"cell_type": "code",
"execution_count": 1,
"metadata": {
"trusted": true
},
"outputs": [],
"source": [
"class Ask():\n",
" def __init__(self,choices=['y','n']):\n",
" self.choices = choices\n",
" def ask(self):\n",
" if max([len(x) for x in self.choices])>1:\n",
" for i,x in enumerate(self.choices):\n",
" print(\"{0}. {1}\".format(i,x),flush=True)\n",
" x = int(input())\n",
" return self.choices[x]\n",
" else:\n",
" print(\"/\".join(self.choices),flush=True)\n",
" return input()\n",
"\n",
"class Content():\n",
" def __init__(self,x):\n",
" self.x=x\n",
" \n",
"class If(Content):\n",
" pass\n",
"\n",
"class AND(Content):\n",
" pass\n",
"\n",
"class OR(Content):\n",
" pass"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"În sistemul nostru, memoria de lucru ar conține lista de **fapte** ca **perechi atribut-valoare**. Baza de cunoștințe poate fi definită ca un dicționar mare care asociază acțiuni (fapte noi care ar trebui inserate în memoria de lucru) cu condiții, exprimate ca expresii AND-OR. De asemenea, unele fapte pot fi `Întrebate`.\n"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {
"trusted": true
},
"outputs": [],
"source": [
"rules = {\n",
" 'default': Ask(['y','n']),\n",
" 'color' : Ask(['red-brown','black and white','other']),\n",
" 'pattern' : Ask(['dark stripes','dark spots']),\n",
" 'mammal': If(OR(['hair','gives milk'])),\n",
" 'carnivor': If(OR([AND(['sharp teeth','claws','forward-looking eyes']),'eats meat'])),\n",
" 'ungulate': If(['mammal',OR(['has hooves','chews cud'])]),\n",
" 'bird': If(OR(['feathers',AND(['flies','lies eggs'])])),\n",
" 'animal:monkey' : If(['mammal','carnivor','color:red-brown','pattern:dark spots']),\n",
" 'animal:tiger' : If(['mammal','carnivor','color:red-brown','pattern:dark stripes']),\n",
" 'animal:giraffe' : If(['ungulate','long neck','long legs','pattern:dark spots']),\n",
" 'animal:zebra' : If(['ungulate','pattern:dark stripes']),\n",
" 'animal:ostrich' : If(['bird','long nech','color:black and white','cannot fly']),\n",
" 'animal:pinguin' : If(['bird','swims','color:black and white','cannot fly']),\n",
" 'animal:albatross' : If(['bird','flies well'])\n",
"}"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Pentru a realiza inferența inversă, vom defini clasa `Knowledgebase`. Aceasta va conține:\n",
"* `Memoria` de lucru - un dicționar care mapă atributele la valori\n",
"* `Regulile` bazei de cunoștințe în formatul definit mai sus\n",
"\n",
"Două metode principale sunt:\n",
"* `get` pentru a obține valoarea unui atribut, efectuând inferența dacă este necesar. De exemplu, `get('color')` ar obține valoarea unui slot de culoare (va întreba dacă este necesar și va stoca valoarea pentru utilizare ulterioară în memoria de lucru). Dacă cerem `get('color:blue')`, va întreba pentru o culoare și apoi va returna valoarea `y`/`n` în funcție de culoare.\n",
"* `eval` efectuează inferența propriu-zisă, adică parcurge arborele AND/OR, evaluează sub-obiectivele etc.\n"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {
"trusted": true
},
"outputs": [],
"source": [
"class KnowledgeBase():\n",
" def __init__(self,rules):\n",
" self.rules = rules\n",
" self.memory = {}\n",
" \n",
" def get(self,name):\n",
" if ':' in name:\n",
" k,v = name.split(':')\n",
" vv = self.get(k)\n",
" return 'y' if v==vv else 'n'\n",
" if name in self.memory.keys():\n",
" return self.memory[name]\n",
" for fld in self.rules.keys():\n",
" if fld==name or fld.startswith(name+\":\"):\n",
" # print(\" + proving {}\".format(fld))\n",
" value = 'y' if fld==name else fld.split(':')[1]\n",
" res = self.eval(self.rules[fld],field=name)\n",
" if res!='y' and res!='n' and value=='y':\n",
" self.memory[name] = res\n",
" return res\n",
" if res=='y':\n",
" self.memory[name] = value\n",
" return value\n",
" # field is not found, using default\n",
" res = self.eval(self.rules['default'],field=name)\n",
" self.memory[name]=res\n",
" return res\n",
" \n",
" def eval(self,expr,field=None):\n",
" # print(\" + eval {}\".format(expr))\n",
" if isinstance(expr,Ask):\n",
" print(field)\n",
" return expr.ask()\n",
" elif isinstance(expr,If):\n",
" return self.eval(expr.x)\n",
" elif isinstance(expr,AND) or isinstance(expr,list):\n",
" expr = expr.x if isinstance(expr,AND) else expr\n",
" for x in expr:\n",
" if self.eval(x)=='n':\n",
" return 'n'\n",
" return 'y'\n",
" elif isinstance(expr,OR):\n",
" for x in expr.x:\n",
" if self.eval(x)=='y':\n",
" return 'y'\n",
" return 'n'\n",
" elif isinstance(expr,str):\n",
" return self.get(expr)\n",
" else:\n",
" print(\"Unknown expr: {}\".format(expr))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Acum să definim baza noastră de cunoștințe despre animale și să efectuăm consultarea. Rețineți că acest apel vă va pune întrebări. Puteți răspunde tastând `y`/`n` pentru întrebările de tip da-nu sau specificând un număr (0..N) pentru întrebările cu răspunsuri multiple mai lungi.\n"
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {
"trusted": true
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"hair\n",
"y/n\n",
"sharp teeth\n",
"y/n\n",
"claws\n",
"y/n\n",
"forward-looking eyes\n",
"y/n\n",
"color\n",
"0. red-brown\n",
"1. black and white\n",
"2. other\n",
"has hooves\n",
"y/n\n",
"long neck\n",
"y/n\n",
"long legs\n",
"y/n\n",
"pattern\n",
"0. dark stripes\n",
"1. dark spots\n"
]
},
{
"data": {
"text/plain": [
"'giraffe'"
]
},
"execution_count": 4,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"kb = KnowledgeBase(rules)\n",
"kb.get('animal')"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Utilizarea PyKnow pentru inferență înainte\n",
"\n",
"În următorul exemplu, vom încerca să implementăm inferența înainte folosind una dintre bibliotecile pentru reprezentarea cunoștințelor, [PyKnow](https://github.com/buguroo/pyknow/). **PyKnow** este o bibliotecă pentru crearea sistemelor de inferență înainte în Python, concepută să fie similară cu vechiul sistem clasic [CLIPS](http://www.clipsrules.net/index.html).\n",
"\n",
"Am fi putut implementa și noi lanțul înainte fără prea multe probleme, dar implementările naive nu sunt, de obicei, foarte eficiente. Pentru o potrivire mai eficientă a regulilor, se folosește un algoritm special, [Rete](https://en.wikipedia.org/wiki/Rete_algorithm).\n"
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {
"trusted": true
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Collecting git+https://github.com/buguroo/pyknow/\n",
" Cloning https://github.com/buguroo/pyknow/ to /tmp/pip-req-build-3cqeulyl\n",
" Running command git clone --filter=blob:none --quiet https://github.com/buguroo/pyknow/ /tmp/pip-req-build-3cqeulyl\n",
" Resolved https://github.com/buguroo/pyknow/ to commit 48818336f2e9a126f1964f2d8dc22d37ff800fe8\n",
" Preparing metadata (setup.py) ... \u001b[?25ldone\n",
"\u001b[?25hCollecting frozendict==1.2\n",
" Using cached frozendict-1.2.tar.gz (2.6 kB)\n",
" Preparing metadata (setup.py) ... \u001b[?25ldone\n",
"\u001b[?25hCollecting schema==0.6.7\n",
" Using cached schema-0.6.7-py2.py3-none-any.whl (14 kB)\n",
"Building wheels for collected packages: pyknow, frozendict\n",
" Building wheel for pyknow (setup.py) ... \u001b[?25ldone\n",
"\u001b[?25h Created wheel for pyknow: filename=pyknow-1.7.0-py3-none-any.whl size=34228 sha256=b7de5b09292c4007667c72f69b98d5a1b5f7324ff15f9dd8e077c3d5f7aade42\n",
" Stored in directory: /tmp/pip-ephem-wheel-cache-k7jpave7/wheels/81/1a/d3/f6c15dbe1955598a37755215f2a10449e7418500d7bd4b9508\n",
" Building wheel for frozendict (setup.py) ... \u001b[?25ldone\n",
"\u001b[?25h Created wheel for frozendict: filename=frozendict-1.2-py3-none-any.whl size=3148 sha256=2863d55c240d2409cddf05ccfe600591f8478681549fc97555c47c90dc6bb160\n",
" Stored in directory: /home/rg/.cache/pip/wheels/49/ac/f8/cb8120244e710bdb479c86198b03c7b08c3c2d3d2bf448fd6e\n",
"Successfully built pyknow frozendict\n",
"Installing collected packages: schema, frozendict, pyknow\n",
"Successfully installed frozendict-1.2 pyknow-1.7.0 schema-0.6.7\n"
]
}
],
"source": [
"import sys\n",
"!{sys.executable} -m pip install git+https://github.com/buguroo/pyknow/"
]
},
{
"cell_type": "code",
"execution_count": 13,
"metadata": {
"trusted": true
},
"outputs": [],
"source": [
"from pyknow import *\n",
"#import pyknow"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Vom defini sistemul nostru ca o clasă care extinde `KnowledgeEngine`. Fiecare regulă este definită printr-o funcție separată cu adnotarea `@Rule`, care specifică momentul în care regula ar trebui să fie declanșată. În interiorul regulii, putem adăuga noi fapte folosind funcția `declare`, iar adăugarea acestor fapte va duce la apelarea unor reguli suplimentare de către motorul de inferență înainte.\n"
]
},
{
"cell_type": "code",
"execution_count": 14,
"metadata": {
"trusted": true
},
"outputs": [],
"source": [
"class Animals(KnowledgeEngine):\n",
" @Rule(OR(\n",
" AND(Fact('sharp teeth'),Fact('claws'),Fact('forward looking eyes')),\n",
" Fact('eats meat')))\n",
" def cornivor(self):\n",
" self.declare(Fact('carnivor'))\n",
" \n",
" @Rule(OR(Fact('hair'),Fact('gives milk')))\n",
" def mammal(self):\n",
" self.declare(Fact('mammal'))\n",
"\n",
" @Rule(Fact('mammal'),\n",
" OR(Fact('has hooves'),Fact('chews cud')))\n",
" def hooves(self):\n",
" self.declare('ungulate')\n",
" \n",
" @Rule(OR(Fact('feathers'),AND(Fact('flies'),Fact('lays eggs'))))\n",
" def bird(self):\n",
" self.declare('bird')\n",
" \n",
" @Rule(Fact('mammal'),Fact('carnivor'),\n",
" Fact(color='red-brown'),\n",
" Fact(pattern='dark spots'))\n",
" def monkey(self):\n",
" self.declare(Fact(animal='monkey'))\n",
"\n",
" @Rule(Fact('mammal'),Fact('carnivor'),\n",
" Fact(color='red-brown'),\n",
" Fact(pattern='dark stripes'))\n",
" def tiger(self):\n",
" self.declare(Fact(animal='tiger'))\n",
"\n",
" @Rule(Fact('ungulate'),\n",
" Fact('long neck'),\n",
" Fact('long legs'),\n",
" Fact(pattern='dark spots'))\n",
" def giraffe(self):\n",
" self.declare(Fact(animal='giraffe'))\n",
"\n",
" @Rule(Fact('ungulate'),\n",
" Fact(pattern='dark stripes'))\n",
" def zebra(self):\n",
" self.declare(Fact(animal='zebra'))\n",
"\n",
" @Rule(Fact('bird'),\n",
" Fact('long neck'),\n",
" Fact('cannot fly'),\n",
" Fact(color='black and white'))\n",
" def straus(self):\n",
" self.declare(Fact(animal='ostrich'))\n",
"\n",
" @Rule(Fact('bird'),\n",
" Fact('swims'),\n",
" Fact('cannot fly'),\n",
" Fact(color='black and white'))\n",
" def pinguin(self):\n",
" self.declare(Fact(animal='pinguin'))\n",
"\n",
" @Rule(Fact('bird'),\n",
" Fact('flies well'))\n",
" def albatros(self):\n",
" self.declare(Fact(animal='albatross'))\n",
" \n",
" @Rule(Fact(animal=MATCH.a))\n",
" def print_result(self,a):\n",
" print('Animal is {}'.format(a))\n",
" \n",
" def factz(self,l):\n",
" for x in l:\n",
" self.declare(x)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Odată ce am definit o bază de cunoștințe, populăm memoria noastră de lucru cu câteva fapte inițiale și apoi apelăm metoda `run()` pentru a efectua inferența. Puteți observa ca rezultat că noi fapte deduse sunt adăugate în memoria de lucru, inclusiv faptul final despre animal (dacă am configurat corect toate faptele inițiale).\n"
]
},
{
"cell_type": "code",
"execution_count": 15,
"metadata": {
"trusted": true
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Animal is tiger\n"
]
},
{
"data": {
"text/plain": [
"FactList([(0, InitialFact()),\n",
" (1, Fact(color='red-brown')),\n",
" (2, Fact(pattern='dark stripes')),\n",
" (3, Fact('sharp teeth')),\n",
" (4, Fact('claws')),\n",
" (5, Fact('forward looking eyes')),\n",
" (6, Fact('gives milk')),\n",
" (7, Fact('mammal')),\n",
" (8, Fact('carnivor')),\n",
" (9, Fact(animal='tiger'))])"
]
},
"execution_count": 15,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"ex1 = Animals()\n",
"ex1.reset()\n",
"ex1.factz([\n",
" Fact(color='red-brown'),\n",
" Fact(pattern='dark stripes'),\n",
" Fact('sharp teeth'),\n",
" Fact('claws'),\n",
" Fact('forward looking eyes'),\n",
" Fact('gives milk')])\n",
"ex1.run()\n",
"ex1.facts"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": []
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"\n---\n\n**Declinare de responsabilitate**: \nAcest document a fost tradus folosind serviciul de traducere AI [Co-op Translator](https://github.com/Azure/co-op-translator). Deși ne străduim să asigurăm acuratețea, vă rugăm să rețineți că traducerile automate pot conține erori sau inexactități. Documentul original în limba sa natală ar trebui considerat sursa autoritară. Pentru informații critice, se recomandă traducerea profesională realizată de un specialist uman. Nu ne asumăm responsabilitatea pentru eventualele neînțelegeri sau interpretări greșite care pot apărea din utilizarea acestei traduceri.\n"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3.7.4 64-bit (conda)",
"metadata": {
"interpreter": {
"hash": "86193a1ab0ba47eac1c69c1756090baa3b420b3eea7d4aafab8b85f8b312f0c5"
}
},
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.11.2"
},
"coopTranslator": {
"original_hash": "ab2bd97b0453415b89a469284609a8ce",
"translation_date": "2025-08-29T23:58:23+00:00",
"source_file": "lessons/2-Symbolic/Animals.ipynb",
"language_code": "ro"
}
},
"nbformat": 4,
"nbformat_minor": 2
}

View File

@ -0,0 +1,595 @@
{
"cells": [
{
"cell_type": "markdown",
"metadata": {
"collapsed": true
},
"source": [
"# Ontologia Relațiilor de Familie\n",
"\n",
"Acest exemplu face parte din [Curriculum-ul AI pentru Începători](http://github.com/microsoft/ai-for-beginners) și a fost inspirat de [această postare pe blog](https://habr.com/post/270857/).\n",
"\n",
"Întotdeauna mi se pare dificil să îmi amintesc diferitele relații dintre oamenii dintr-o familie. În acest exemplu, vom lua o ontologie care definește relațiile de familie și arborele genealogic propriu-zis și vom arăta cum putem efectua inferențe automate pentru a găsi toți rudele.\n",
"\n",
"### Obținerea Arborelui Genealogic\n",
"\n",
"Ca exemplu, vom lua arborele genealogic al [Familiei Țarilor Romanov](https://en.wikipedia.org/wiki/House_of_Romanov). Cel mai comun format pentru descrierea relațiilor de familie este [GEDCOM](https://en.wikipedia.org/wiki/GEDCOM). Vom folosi arborele genealogic al familiei Romanov în format GEDCOM:\n"
]
},
{
"cell_type": "code",
"execution_count": 1,
"metadata": {
"trusted": true
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"0 HEAD\n",
"1 CHAR UTF8\n",
"1 GEDC\n",
"2 VERS 5.5\n",
"0 @0@ INDI\n",
"1 NAME Mihail Fedorovich /Romanov/\n",
"1 SEX M\n",
"1 BIRT\n",
"2 DATE 1613\n",
"1 DEAT \n",
"2 DATE 1645\n",
"1 FAMS @41@\n",
"0 @1@ INDI\n",
"1 NAME Evdokija Lukjanovna /Streshneva/\n",
"1 SEX F\n"
]
}
],
"source": [
"!head -15 data/tsars.ged"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Pentru a utiliza fișierul GEDCOM, putem folosi biblioteca `python-gedcom`:\n"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {
"trusted": true
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Collecting python-gedcom\n",
" Downloading python_gedcom-1.0.0-py2.py3-none-any.whl (35 kB)\n",
"Installing collected packages: python-gedcom\n",
"Successfully installed python-gedcom-1.0.0\n"
]
}
],
"source": [
"import sys\n",
"!{sys.executable} -m pip install python-gedcom"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Această bibliotecă elimină unele dintre problemele tehnice legate de analizarea fișierelor, dar ne oferă în continuare acces la nivel destul de jos la toți indivizii și familiile din arbore. Iată cum putem analiza fișierul și afișa lista tuturor indivizilor:\n"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {
"trusted": true
},
"outputs": [],
"source": [
"from gedcom.parser import Parser\n",
"from gedcom.element.individual import IndividualElement\n",
"from gedcom.element.family import FamilyElement\n",
"g = Parser()\n",
"g.parse_file('data/tsars.ged')"
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {
"scrolled": true,
"trusted": true
},
"outputs": [
{
"data": {
"text/plain": [
"[('@0@', ('Mihail Fedorovich', 'Romanov')),\n",
" ('@1@', ('Evdokija Lukjanovna', 'Streshneva')),\n",
" ('@2@', ('Aleksej Mihajlovich', 'Romanov')),\n",
" ('@3@', ('Marija Ilinichna', 'Miloslavskaja')),\n",
" ('@4@', ('Natalja Kirillovna', 'Naryshkina')),\n",
" ('@5@', ('Marfa Matveevna', 'Apraksina')),\n",
" ('@6@', ('Fedor Alekseevich', 'Romanov')),\n",
" ('@7@', ('Sofja Aleksevna', 'Romanova')),\n",
" ('@8@', ('Ivan V Alekseevich', 'Romanov')),\n",
" ('@9@', ('Praskovja Fedorovna', 'Saltykova')),\n",
" ('@10@', ('Ekaterina Ivanovna', 'Romanova')),\n",
" ('@11@', ('Anna Ivanovna', 'Romanova')),\n",
" ('@12@', ('Fridrih Vilgelm', 'Kurlandskij')),\n",
" ('@13@', ('Karl Leopold', 'Meklenburg-Shverinskij')),\n",
" ('@14@', ('Anna Leopoldovna', 'Meklenburg-Shverinskaja')),\n",
" ('@15@', ('Anton Ulrih', 'Braunshvejg-Volfenbjuttelskij')),\n",
" ('@16@', ('Ivan VI Antonovich', 'Braunshvejg-Volfenbjuttelskij')),\n",
" ('@17@', ('Petr I Alekseevich', 'Romanov')),\n",
" ('@18@', ('Evdokija Fedorovna', 'Lopuhina')),\n",
" ('@19@', ('Ekaterina I Alekseevna', 'Mihajlova')),\n",
" ('@20@', ('Aleksej Petrovich', 'Romanov')),\n",
" ('@21@', ('Sharlotta Kristina', 'Braunshvejg-Volfenbjuttelskaja')),\n",
" ('@22@', ('Petr II Alekseevich', 'Romanov')),\n",
" ('@23@', ('Anna Petrovna', 'Romanova')),\n",
" ('@24@', ('Elizaveta Petrovna', 'Romanova')),\n",
" ('@25@', ('Karl Fridrih', 'Golshtejn-Gottorpskij')),\n",
" ('@26@', ('Petr III Fedorovich', 'Romanov')),\n",
" ('@27@', ('Ekaterina II', 'Alekseevna')),\n",
" ('@28@', ('Pavel I Petrovich', 'Romanov')),\n",
" ('@29@', ('Natalja Alekseevna', 'Gessen-Darmshtadskaja')),\n",
" ('@30@', ('Marija Fedorovna', 'Vjurtembergskaja')),\n",
" ('@31@', ('Aleksandr I Pavlovich', 'Romanov')),\n",
" ('@32@', ('Elizaveta Alekseevna', 'Baden-Durlahskaja')),\n",
" ('@33@', ('Nikolaj I Pavlovich', 'Romanov')),\n",
" ('@34@', ('Aleksandra Fedorovna', 'Prusskaja')),\n",
" ('@35@', ('Aleksandr II Nikolaevich', 'Romanov')),\n",
" ('@36@', ('Marija Aleksandrovna', 'Gessenskaja')),\n",
" ('@37@', ('Aleksandr III Aleksandrovich', 'Romanov')),\n",
" ('@38@', ('Marija Fedorovna', 'Datskaja')),\n",
" ('@39@', ('Nikolaj II Aleksandrovich', 'Romanov')),\n",
" ('@40@', ('Aleksandra Fedorovna', 'Gessenskaja'))]"
]
},
"execution_count": 4,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"d = g.get_element_dictionary()\n",
"[ (k,v.get_name()) for k,v in d.items() if isinstance(v,IndividualElement)]"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Iată cum putem obține informații despre familii. Rețineți că aceasta ne oferă o listă de **identificatori**, și trebuie să îi convertim în nume dacă dorim mai multă claritate:\n"
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"[('@41@', ['@0@', '@1@', '@2@']),\n",
" ('@42@', ['@2@', '@3@', '@6@', '@7@', '@8@']),\n",
" ('@43@', ['@8@', '@9@', '@10@', '@11@']),\n",
" ('@44@', ['@13@', '@10@', '@14@']),\n",
" ('@45@', ['@15@', '@14@', '@16@']),\n",
" ('@46@', ['@2@', '@4@', '@17@']),\n",
" ('@47@', ['@17@', '@18@', '@20@']),\n",
" ('@48@', ['@20@', '@21@', '@22@']),\n",
" ('@49@', ['@17@', '@19@', '@23@', '@24@']),\n",
" ('@50@', ['@25@', '@23@', '@26@']),\n",
" ('@51@', ['@26@', '@27@', '@28@']),\n",
" ('@52@', ['@28@', '@30@', '@31@', '@33@']),\n",
" ('@53@', ['@33@', '@34@', '@35@']),\n",
" ('@54@', ['@35@', '@36@', '@37@']),\n",
" ('@55@', ['@37@', '@38@', '@39@'])]"
]
},
"execution_count": 5,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"d = g.get_element_dictionary()\n",
"[ (k,[x.get_value() for x in v.get_child_elements()]) for k,v in d.items() if isinstance(v,FamilyElement)]"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Obținerea Ontologiei Familiei\n",
"\n",
"În continuare, să aruncăm o privire asupra [ontologiei familiei](https://raw.githubusercontent.com/blokhin/genealogical-trees/master/data/header.ttl) definită ca un set de triplete ale Web-ului Semantic. Această ontologie definește relații precum `isUncleOf`, `isCousinOf` și multe altele. Toate aceste relații sunt definite în termeni de predicate de bază `isMotherOf`, `isFatherOf`, `isBrotherOf` și `isSisterOf`. Vom folosi raționamentul automat pentru a deduce toate celelalte relații utilizând ontologia.\n",
"\n",
"Iată o definiție exemplară a proprietății `isAuntOf`, care este definită ca o compoziție între `isSisterOf` și `isParentOf` (*Mătușa este sora părintelui cuiva*).\n",
"\n",
"```\n",
"fhkb:isAuntOf a owl:ObjectProperty ;\n",
" rdfs:domain fhkb:Woman ;\n",
" rdfs:range fhkb:Person ;\n",
" owl:propertyChainAxiom ( fhkb:isSisterOf fhkb:isParentOf ) .\n",
"```\n"
]
},
{
"cell_type": "code",
"execution_count": 6,
"metadata": {
"trusted": true
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"@prefix fhkb: <http://www.example.com/genealogy.owl#> .\n",
"@prefix owl: <http://www.w3.org/2002/07/owl#> .\n",
"@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .\n",
"@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .\n",
"@prefix xml: <http://www.w3.org/XML/1998/namespace> .\n",
"@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .\n",
"\n",
"<http://www.example.com/genealogy.owl#> a owl:Ontology .\n",
"\n",
"fhkb:DomainEntity a owl:Class .\n",
"\n",
"fhkb:Man a owl:Class ;\n",
" owl:equivalentClass [ a owl:Class ;\n",
" owl:intersectionOf ( fhkb:Person [ a owl:Restriction ;\n",
" owl:onProperty fhkb:hasSex ;\n",
" owl:someValuesFrom fhkb:Male ] ) ] .\n",
"\n",
"fhkb:Woman a owl:Class ;\n",
" owl:equivalentClass [ a owl:Class ;\n",
" owl:intersectionOf ( fhkb:Person [ a owl:Restriction ;\n"
]
}
],
"source": [
"!head -20 data/onto.ttl"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Construirea unei ontologii pentru inferență\n",
"\n",
"Pentru simplitate, vom crea un singur fișier de ontologie care va include regulile originale din ontologia familiei și faptele despre indivizi din fișierul nostru GEDCOM. Vom parcurge fișierul GEDCOM, vom extrage informații despre familii și indivizi și le vom converti în triplete.\n"
]
},
{
"cell_type": "code",
"execution_count": 7,
"metadata": {
"trusted": true
},
"outputs": [],
"source": [
"!cp data/onto.ttl .\n",
"\n",
"gedcom_dict = g.get_element_dictionary()\n",
"individuals, marriages = {}, {}\n",
"\n",
"def term2id(el):\n",
" return \"i\" + el.get_pointer().replace('@', '').lower()\n",
"\n",
"out = open(\"onto.ttl\",\"a\")\n",
"\n",
"for k, v in gedcom_dict.items():\n",
" if isinstance(v,IndividualElement):\n",
" children, siblings = set(), set()\n",
" idx = term2id(v)\n",
"\n",
" title = v.get_name()[0] + \" \" + v.get_name()[1]\n",
" title = title.replace('\"', '').replace('[', '').replace(']', '').replace('(', '').replace(')', '').strip()\n",
"\n",
" own_families = g.get_families(v, 'FAMS')\n",
" for fam in own_families:\n",
" children |= set(term2id(i) for i in g.get_family_members(fam, \"CHIL\"))\n",
"\n",
" parent_families = g.get_families(v, 'FAMC')\n",
" if len(parent_families):\n",
" for member in g.get_family_members(parent_families[0], \"CHIL\"): # NB adoptive families i.e len(parent_families)>1 are not considered (TODO?)\n",
" if member.get_pointer() == v.get_pointer():\n",
" continue\n",
" siblings.add(term2id(member))\n",
"\n",
" if idx in individuals:\n",
" children |= individuals[idx].get('children', set())\n",
" siblings |= individuals[idx].get('siblings', set())\n",
" individuals[idx] = {'sex': v.get_gender().lower(), 'children': children, 'siblings': siblings, 'title': title}\n",
"\n",
" elif isinstance(v,FamilyElement):\n",
" wife, husb, children = None, None, set()\n",
" children = set(term2id(i) for i in g.get_family_members(v, \"CHIL\"))\n",
"\n",
" try:\n",
" wife = g.get_family_members(v, \"WIFE\")[0]\n",
" wife = term2id(wife)\n",
" if wife in individuals: individuals[wife]['children'] |= children\n",
" else: individuals[wife] = {'children': children}\n",
" except IndexError: pass\n",
" try:\n",
" husb = g.get_family_members(v, \"HUSB\")[0]\n",
" husb = term2id(husb)\n",
" if husb in individuals: individuals[husb]['children'] |= children\n",
" else: individuals[husb] = {'children': children}\n",
" except IndexError: pass\n",
"\n",
" if wife and husb: marriages[wife + husb] = (term2id(v), wife, husb)\n",
"\n",
"for idx, val in individuals.items():\n",
" added_terms = ''\n",
" if val['sex'] == 'f':\n",
" parent_predicate, sibl_predicate = \"isMotherOf\", \"isSisterOf\"\n",
" else:\n",
" parent_predicate, sibl_predicate = \"isFatherOf\", \"isBrotherOf\"\n",
" if len(val['children']):\n",
" added_terms += \" ;\\n fhkb:\" + parent_predicate + \" \" + \", \".join([\"fhkb:\" + i for i in val['children']])\n",
" if len(val['siblings']):\n",
" added_terms += \" ;\\n fhkb:\" + sibl_predicate + \" \" + \", \".join([\"fhkb:\" + i for i in val['siblings']])\n",
" out.write(\"fhkb:%s a owl:NamedIndividual, owl:Thing%s ;\\n rdfs:label \\\"%s\\\" .\\n\" % (idx, added_terms, val['title']))\n",
"\n",
"for k, v in marriages.items():\n",
" out.write(\"fhkb:%s a owl:NamedIndividual, owl:Thing ;\\n fhkb:hasFemalePartner fhkb:%s ;\\n fhkb:hasMalePartner fhkb:%s .\\n\" % v)\n",
"\n",
"out.write(\"[] a owl:AllDifferent ;\\n owl:distinctMembers (\")\n",
"for idx in individuals.keys():\n",
" out.write(\" fhkb:\" + idx)\n",
"for k, v in marriages.items():\n",
" out.write(\" fhkb:\" + v[0])\n",
"out.write(\" ) .\")\n",
"out.close()"
]
},
{
"cell_type": "code",
"execution_count": 8,
"metadata": {
"trusted": true
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
" fhkb:hasFemalePartner fhkb:i34 ;\n",
" fhkb:hasMalePartner fhkb:i33 .\n",
"fhkb:i54 a owl:NamedIndividual, owl:Thing ;\n",
" fhkb:hasFemalePartner fhkb:i36 ;\n",
" fhkb:hasMalePartner fhkb:i35 .\n",
"fhkb:i55 a owl:NamedIndividual, owl:Thing ;\n",
" fhkb:hasFemalePartner fhkb:i38 ;\n",
" fhkb:hasMalePartner fhkb:i37 .\n",
"[] a owl:AllDifferent ;\n",
" owl:distinctMembers ( fhkb:i0 fhkb:i1 fhkb:i2 fhkb:i3 fhkb:i4 fhkb:i5 fhkb:i6 fhkb:i7 fhkb:i8 fhkb:i9 fhkb:i10 fhkb:i11 fhkb:i12 fhkb:i13 fhkb:i14 fhkb:i15 fhkb:i16 fhkb:i17 fhkb:i18 fhkb:i19 fhkb:i20 fhkb:i21 fhkb:i22 fhkb:i23 fhkb:i24 fhkb:i25 fhkb:i26 fhkb:i27 fhkb:i28 fhkb:i29 fhkb:i30 fhkb:i31 fhkb:i32 fhkb:i33 fhkb:i34 fhkb:i35 fhkb:i36 fhkb:i37 fhkb:i38 fhkb:i39 fhkb:i40 fhkb:i41 fhkb:i42 fhkb:i43 fhkb:i44 fhkb:i45 fhkb:i46 fhkb:i47 fhkb:i48 fhkb:i49 fhkb:i50 fhkb:i51 fhkb:i52 fhkb:i53 fhkb:i54 fhkb:i55 ) ."
]
}
],
"source": [
"!tail onto.ttl"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Realizarea inferenței\n",
"\n",
"Acum dorim să putem utiliza această ontologie pentru inferență și interogare. Vom folosi [RDFLib](https://github.com/RDFLib), o bibliotecă pentru citirea graficelor RDF în diferite formate, interogarea acestora etc.\n",
"\n",
"Pentru inferența logică, vom folosi biblioteca [OWL-RL](https://github.com/RDFLib/OWL-RL), care ne permite să construim **Closure** al graficului RDF, adică să adăugăm toate conceptele și relațiile posibile care pot fi deduse.\n"
]
},
{
"cell_type": "code",
"execution_count": 10,
"metadata": {
"trusted": true
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Requirement already satisfied: rdflib in /home/rg/anaconda3/envs/ai4beg/lib/python3.11/site-packages (6.3.2)\n",
"Requirement already satisfied: isodate<0.7.0,>=0.6.0 in /home/rg/anaconda3/envs/ai4beg/lib/python3.11/site-packages (from rdflib) (0.6.1)\n",
"Requirement already satisfied: pyparsing<4,>=2.1.0 in /home/rg/anaconda3/envs/ai4beg/lib/python3.11/site-packages (from rdflib) (3.0.9)\n",
"Requirement already satisfied: six in /home/rg/anaconda3/envs/ai4beg/lib/python3.11/site-packages (from isodate<0.7.0,>=0.6.0->rdflib) (1.16.0)\n",
"Collecting git+https://github.com/RDFLib/OWL-RL.git\n",
" Cloning https://github.com/RDFLib/OWL-RL.git to /tmp/pip-req-build-lbfzwi3m\n",
" Running command git clone --filter=blob:none --quiet https://github.com/RDFLib/OWL-RL.git /tmp/pip-req-build-lbfzwi3m\n",
" Resolved https://github.com/RDFLib/OWL-RL.git to commit a77e1791b88b54aace609bc6000aac14c7add4ff\n",
" Preparing metadata (setup.py) ... \u001b[?25ldone\n",
"\u001b[?25hRequirement already satisfied: rdflib>=6.0.2 in /home/rg/anaconda3/envs/ai4beg/lib/python3.11/site-packages (from owlrl==6.0.2) (6.3.2)\n",
"Requirement already satisfied: isodate<0.7.0,>=0.6.0 in /home/rg/anaconda3/envs/ai4beg/lib/python3.11/site-packages (from rdflib>=6.0.2->owlrl==6.0.2) (0.6.1)\n",
"Requirement already satisfied: pyparsing<4,>=2.1.0 in /home/rg/anaconda3/envs/ai4beg/lib/python3.11/site-packages (from rdflib>=6.0.2->owlrl==6.0.2) (3.0.9)\n",
"Requirement already satisfied: six in /home/rg/anaconda3/envs/ai4beg/lib/python3.11/site-packages (from isodate<0.7.0,>=0.6.0->rdflib>=6.0.2->owlrl==6.0.2) (1.16.0)\n"
]
}
],
"source": [
"!{sys.executable} -m pip install rdflib\n",
"!{sys.executable} -m pip install git+https://github.com/RDFLib/OWL-RL.git"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Să deschidem fișierul de ontologie și să vedem câte triplete conține:\n"
]
},
{
"cell_type": "code",
"execution_count": 11,
"metadata": {
"trusted": true
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Triplets found:669\n"
]
}
],
"source": [
"import rdflib\n",
"from owlrl import DeductiveClosure, OWLRL_Extension\n",
"\n",
"g = rdflib.Graph()\n",
"g.parse(\"onto.ttl\", format=\"turtle\")\n",
"\n",
"print(\"Triplets found:%d\" % len(g))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Acum să construim închiderea și să vedem cum crește numărul de triplete:\n"
]
},
{
"cell_type": "code",
"execution_count": 12,
"metadata": {
"trusted": true
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Triplets after inference:4246\n"
]
}
],
"source": [
"DeductiveClosure(OWLRL_Extension).expand(g)\n",
"print(\"Triplets after inference:%d\" % len(g))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Interogarea pentru Rude\n",
"\n",
"Acum putem interoga graful pentru a vedea diferite relații între persoane. Putem folosi limbajul **SPARQL** împreună cu metoda `query`. În cazul nostru, să vedem toți **unchii** din arborele nostru genealogic:\n"
]
},
{
"cell_type": "code",
"execution_count": 13,
"metadata": {
"trusted": true
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Fedor Alekseevich Romanov is uncle of Ekaterina Ivanovna Romanova\n",
"Aleksandr I Pavlovich Romanov is uncle of Aleksandr II Nikolaevich Romanov\n",
"Fedor Alekseevich Romanov is uncle of Anna Ivanovna Romanova\n"
]
}
],
"source": [
"qres = g.query(\n",
" \"\"\"SELECT DISTINCT ?aname ?bname\n",
" WHERE {\n",
" ?a fhkb:isUncleOf ?b .\n",
" ?a rdfs:label ?aname .\n",
" ?b rdfs:label ?bname .\n",
" }\"\"\")\n",
"\n",
"for row in qres:\n",
" print(\"%s is uncle of %s\" % row)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Simte-te liber să experimentezi cu diferite alte relații de familie. De exemplu, poți arunca o privire la relația `isAncestorOf`, care definește recursiv toți strămoșii unei persoane date.\n",
"\n",
"În final, să facem curățenie!\n"
]
},
{
"cell_type": "code",
"execution_count": 14,
"metadata": {
"trusted": true
},
"outputs": [],
"source": [
"!rm onto.ttl"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": []
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"\n---\n\n**Declinare de responsabilitate**: \nAcest document a fost tradus folosind serviciul de traducere AI [Co-op Translator](https://github.com/Azure/co-op-translator). Deși ne străduim să asigurăm acuratețea, vă rugăm să fiți conștienți că traducerile automate pot conține erori sau inexactități. Documentul original în limba sa natală ar trebui considerat sursa autoritară. Pentru informații critice, se recomandă traducerea profesională realizată de un specialist uman. Nu ne asumăm responsabilitatea pentru eventualele neînțelegeri sau interpretări greșite care pot apărea din utilizarea acestei traduceri.\n"
]
}
],
"metadata": {
"interpreter": {
"hash": "86193a1ab0ba47eac1c69c1756090baa3b420b3eea7d4aafab8b85f8b312f0c5"
},
"kernelspec": {
"display_name": "Python 3.6",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.11.2"
},
"coopTranslator": {
"original_hash": "6537d5597320e27b6052b4377b8ff8bb",
"translation_date": "2025-08-29T23:55:31+00:00",
"source_file": "lessons/2-Symbolic/FamilyOntology.ipynb",
"language_code": "ro"
}
},
"nbformat": 4,
"nbformat_minor": 2
}

View File

@ -0,0 +1,548 @@
{
"cells": [
{
"cell_type": "markdown",
"metadata": {
"collapsed": true
},
"source": [
"## Microsoft Concept Graph\n",
"\n",
"[Microsoft Concept Graph](https://concept.research.microsoft.com/) este o taxonomie extinsă de termeni extrași de pe internet, cu relații de tip `is-a` între concepte.\n",
"\n",
"Context Graph este disponibil în două forme:\n",
" * Fișier text mare pentru descărcare\n",
" * REST API\n",
"\n",
"Statistici:\n",
" * 5401933 concepte unice,\n",
" * 12551613 instanțe unice,\n",
" * 87603947 relații de tip `is-a`\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Utilizarea serviciului web\n",
"\n",
"Serviciul web oferă diferite apeluri pentru a estima probabilitatea ca un concept să aparțină unor grupuri diferite. Mai multe informații sunt disponibile [aici](https://concept.research.microsoft.com/Home/Api). \n",
"Iată un exemplu de URL pentru apel: `https://concept.research.microsoft.com/api/Concept/ScoreByProb?instance=microsoft&topK=10`\n"
]
},
{
"cell_type": "code",
"execution_count": 7,
"metadata": {
"trusted": true
},
"outputs": [
{
"data": {
"text/plain": [
"{'company': 0.6105356614382954,\n",
" 'vendor': 0.08858636677518003,\n",
" 'client': 0.048239124001183784,\n",
" 'firm': 0.045476965571668145,\n",
" 'large company': 0.043109401203511886,\n",
" 'organization': 0.043010752688172046,\n",
" 'corporation': 0.035908059583703265,\n",
" 'brand': 0.03383644076156654,\n",
" 'software company': 0.027522935779816515,\n",
" 'technology company': 0.023774292196902438}"
]
},
"execution_count": 7,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"import urllib\n",
"import json\n",
"import ssl\n",
"\n",
"def http(x):\n",
" ssl._create_default_https_context = ssl._create_unverified_context\n",
" response = urllib.request.urlopen(x)\n",
" data = response.read()\n",
" return data.decode('utf-8')\n",
"\n",
"def query(x):\n",
" return json.loads(http(\"https://concept.research.microsoft.com/api/Concept/ScoreByProb?instance={}&topK=10\".format(urllib.parse.quote(x))))\n",
"\n",
"query('microsoft')"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Să încercăm să clasificăm titlurile știrilor folosind concepte părinte. Pentru a obține titlurile știrilor, vom folosi serviciul [NewsApi.org](http://newsapi.org). Trebuie să obțineți propria cheie API pentru a utiliza serviciul - accesați site-ul web și înregistrați-vă pentru planul de dezvoltator gratuit.\n"
]
},
{
"cell_type": "code",
"execution_count": 20,
"metadata": {
"trusted": true
},
"outputs": [],
"source": [
"newsapi_key = '<your API key here>'\n",
"def get_news(country='us'):\n",
" res = json.loads(http(\"https://newsapi.org/v2/top-headlines?country={0}&apiKey={1}\".format(country,newsapi_key)))\n",
" return res['articles']\n",
"\n",
"all_titles = [x['title'] for x in get_news('us')+get_news('gb')]"
]
},
{
"cell_type": "code",
"execution_count": 21,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"['Covid-19 Live Updates: Vaccines and Boosters News - The New York Times',\n",
" 'Ukrainians Flee Mariupol as Russian Forces Push to Take Port City - The Wall Street Journal',\n",
" 'Bond Yields Jump, Stock Futures Rise After Powell Says Fed Is Ready to Be More Aggressive - The Wall Street Journal',\n",
" 'Putin critic Alexei Navalny found guilty by Russian court - New York Post ',\n",
" \"Supreme Court nominee Ketanji Brown Jackson will face questions at confirmation hearing's second day - CNN\",\n",
" '2 teachers killed at Swedish high school, student arrested - ABC News',\n",
" 'Clues to Covid-19s Next Moves Come From Sewers - The Wall Street Journal',\n",
" 'Republicans to roll dice by grilling Jackson over child-pornography sentencing decisions | TheHill - The Hill',\n",
" 'Clear sign Putin considering using chemical weapons in Ukraine, claims President Biden - The Independent',\n",
" 'NASA confirms there are 5,000 planets outside our solar system - Daily Mail',\n",
" \"US stocks whipsawed overnight after Fed Chair Powell's remarks - Fox Business\",\n",
" \"'We've learned absolutely nothing': Tests could again be in short supply if Covid surges - POLITICO\",\n",
" \"Duchess of Cambridge swaps khaki jungle gear for Vampire's Wife dress on Belize trip - Daily Mail\",\n",
" 'China searches for victims, flight recorders after first plane crash in 12 years - Reuters',\n",
" 'Second superyacht linked to Russian oligarch Abramovich docks in Turkey - Reuters',\n",
" 'Live updates: Russia stops talks with Japan over sanctions - The Associated Press - en Español',\n",
" 'Powers Remain and Threats Lurk as Womens Sweet 16 Is Set - The New York Times',\n",
" 'Webb Space Telescope Begins Multi-Instrument Alignment - SciTechDaily',\n",
" \"UConn vs UCF - NCAA women's tournament second-round highlights - March Madness\",\n",
" 'Bucking Republican Trend, Indiana Governor Vetoes Transgender Sports Bill - The New York Times',\n",
" \"Maggie Fox dead: Coronation Street and Shameless actress dies after 'sudden accident' - Mirror Online - The Mirror\",\n",
" 'China plane crash live: Search for survivors continues as witness describes moment flight fell from sky - The Independent',\n",
" 'Daniel Morgan murder: damning report condemns Met police - The Guardian',\n",
" 'What to expect from Rishi Sunaks Spring Statement - BBC.com',\n",
" 'UK and Republic of Ireland in line to host Euro 2028 after no one else bids - The Guardian',\n",
" \"Friends beg Vladimir Putin's 'lover' to persuade him to end Ukraine invasion - The Mirror\",\n",
" 'Brass Eyes outtakes show the brutal TV comedy was the tip of an iceberg - The Guardian',\n",
" \"Vladimir Putin threatens civilians to break Mariupol's spirit - The Times\",\n",
" 'Shell U-turn on Cambo oilfield would threaten green targets, say campaigners - The Guardian',\n",
" 'St Helens dog attack: Girl aged 17 months killed at home - BBC',\n",
" \"PlayStation to buy 'Assassin's Creed' veteran Jade Raymond's Haven Studios - NME\",\n",
" 'Clear sign Putin considering using chemical weapons in Ukraine, claims President Biden - The Independent',\n",
" 'NASA confirms there are 5,000 planets outside our solar system - Daily Mail',\n",
" 'Nintendo Switch finally has folders • Eurogamer.net - Eurogamer.net',\n",
" 'FA to “find a solution” as Liverpool fan group blasts “shambolic” Wembley travel - This Is Anfield',\n",
" 'Manchester United transfer news LIVE Erik ten Hag latest and Man Utd manager updates - Manchester Evening News',\n",
" 'Inflation raises cost of UK government borrowing in February; crude oil up again business live - The Guardian',\n",
" 'Alexei Navalny: Kremlin critic found guilty of large-scale fraud and contempt of court by Russian court - Sky News',\n",
" \"UK prepares to nationalize Russia natural gas giant Gazprom's retail unit - Business Insider\",\n",
" 'Zaghari-Ratcliffe: Hunt calls for inquiry into delay over Iran debt payment - The Guardian']"
]
},
"execution_count": 21,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"all_titles"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"În primul rând, vrem să putem extrage substantive din titlurile știrilor. Vom folosi biblioteca `TextBlob` pentru a face acest lucru, care simplifică multe dintre sarcinile tipice de procesare a limbajului natural, cum ar fi aceasta.\n"
]
},
{
"cell_type": "code",
"execution_count": 15,
"metadata": {
"trusted": true
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Requirement already satisfied: textblob in c:\\winapp\\miniconda3\\lib\\site-packages (0.17.1)\n",
"Requirement already satisfied: nltk>=3.1 in c:\\winapp\\miniconda3\\lib\\site-packages (from textblob) (3.5)\n",
"Requirement already satisfied: joblib in c:\\winapp\\miniconda3\\lib\\site-packages (from nltk>=3.1->textblob) (1.0.1)\n",
"Requirement already satisfied: regex in c:\\winapp\\miniconda3\\lib\\site-packages (from nltk>=3.1->textblob) (2021.11.10)\n",
"Requirement already satisfied: tqdm in c:\\winapp\\miniconda3\\lib\\site-packages (from nltk>=3.1->textblob) (4.61.2)\n",
"Requirement already satisfied: click in c:\\winapp\\miniconda3\\lib\\site-packages (from nltk>=3.1->textblob) (8.0.3)\n",
"Requirement already satisfied: colorama in c:\\winapp\\miniconda3\\lib\\site-packages (from click->nltk>=3.1->textblob) (0.4.4)\n",
"Finished.\n"
]
},
{
"name": "stderr",
"output_type": "stream",
"text": [
"[nltk_data] Downloading package brown to\n",
"[nltk_data] C:\\Users\\dmitryso\\AppData\\Roaming\\nltk_data...\n",
"[nltk_data] Package brown is already up-to-date!\n",
"[nltk_data] Downloading package punkt to\n",
"[nltk_data] C:\\Users\\dmitryso\\AppData\\Roaming\\nltk_data...\n",
"[nltk_data] Package punkt is already up-to-date!\n",
"[nltk_data] Downloading package wordnet to\n",
"[nltk_data] C:\\Users\\dmitryso\\AppData\\Roaming\\nltk_data...\n",
"[nltk_data] Package wordnet is already up-to-date!\n",
"[nltk_data] Downloading package averaged_perceptron_tagger to\n",
"[nltk_data] C:\\Users\\dmitryso\\AppData\\Roaming\\nltk_data...\n",
"[nltk_data] Package averaged_perceptron_tagger is already up-to-\n",
"[nltk_data] date!\n",
"[nltk_data] Downloading package conll2000 to\n",
"[nltk_data] C:\\Users\\dmitryso\\AppData\\Roaming\\nltk_data...\n",
"[nltk_data] Package conll2000 is already up-to-date!\n",
"[nltk_data] Downloading package movie_reviews to\n",
"[nltk_data] C:\\Users\\dmitryso\\AppData\\Roaming\\nltk_data...\n",
"[nltk_data] Package movie_reviews is already up-to-date!\n"
]
}
],
"source": [
"import sys\n",
"!{sys.executable} -m pip install textblob\n",
"!{sys.executable} -m textblob.download_corpora\n",
"from textblob import TextBlob"
]
},
{
"cell_type": "code",
"execution_count": 22,
"metadata": {
"trusted": true
},
"outputs": [
{
"data": {
"text/plain": [
"{'covid-19 live updates': 1,\n",
" 'vaccines': 1,\n",
" 'boosters': 1,\n",
" 'york': 4,\n",
" 'ukrainians flee mariupol': 1,\n",
" 'forces push': 1,\n",
" 'port city': 1,\n",
" 'wall street journal': 3,\n",
" 'bond yields': 1,\n",
" 'futures rise': 1,\n",
" 'powell says fed': 1,\n",
" 'ready': 1,\n",
" 'be': 1,\n",
" 'aggressive': 1,\n",
" 'putin': 3,\n",
" 'alexei navalny': 2,\n",
" 'russian': 2,\n",
" 'supreme court nominee': 1,\n",
" 'ketanji brown jackson': 1,\n",
" \"confirmation hearing 's\": 1,\n",
" 'cnn': 1,\n",
" 'swedish': 1,\n",
" 'high school': 1,\n",
" 'abc': 1,\n",
" 'clues': 1,\n",
" 'covid-19': 1,\n",
" ' s': 2,\n",
" 'moves': 1,\n",
" 'sewers': 1,\n",
" 'roll dice': 1,\n",
" 'jackson': 1,\n",
" 'decisions |': 1,\n",
" 'thehill': 1,\n",
" 'clear': 2,\n",
" 'chemical weapons': 2,\n",
" 'ukraine': 3,\n",
" 'claims president': 2,\n",
" 'biden': 2,\n",
" 'nasa': 2,\n",
" 'solar system': 2,\n",
" 'daily mail': 3,\n",
" 'us stocks': 1,\n",
" 'fed chair powell': 1,\n",
" \"'s remarks\": 1,\n",
" 'fox': 1,\n",
" \"'we 've\": 1,\n",
" 'tests': 1,\n",
" 'covid': 1,\n",
" 'politico': 1,\n",
" 'duchess': 1,\n",
" 'cambridge': 1,\n",
" 'swaps khaki jungle gear': 1,\n",
" 'vampire': 1,\n",
" 'wife': 1,\n",
" 'belize': 1,\n",
" 'china': 2,\n",
" 'flight recorders': 1,\n",
" 'plane crash': 1,\n",
" 'reuters': 2,\n",
" 'russian oligarch': 1,\n",
" 'abramovich': 1,\n",
" 'live': 1,\n",
" 'russia': 2,\n",
" 'stops talks': 1,\n",
" 'japan': 1,\n",
" 'español': 1,\n",
" 'powers remain': 1,\n",
" 'threats lurk': 1,\n",
" 'set': 1,\n",
" 'webb': 1,\n",
" 'telescope begins multi-instrument alignment': 1,\n",
" 'scitechdaily': 1,\n",
" 'uconn': 1,\n",
" 'ucf': 1,\n",
" 'ncaa': 1,\n",
" \"women 's tournament second-round highlights\": 1,\n",
" 'march madness': 1,\n",
" 'bucking republican trend': 1,\n",
" 'indiana': 1,\n",
" 'vetoes transgender': 1,\n",
" 'bill': 1,\n",
" 'maggie fox': 1,\n",
" 'coronation': 1,\n",
" 'shameless': 1,\n",
" \"'sudden accident\": 1,\n",
" 'mirror online': 1,\n",
" 'mirror': 2,\n",
" 'plane crash ': 1,\n",
" 'search': 1,\n",
" 'moment flight': 1,\n",
" 'daniel morgan': 1,\n",
" 'report condemns': 1,\n",
" 'met': 1,\n",
" 'guardian': 6,\n",
" 'rishi sunak': 1,\n",
" ' s spring': 1,\n",
" 'statement': 1,\n",
" 'bbc.com': 1,\n",
" 'uk': 3,\n",
" 'ireland': 1,\n",
" 'euro': 1,\n",
" 'vladimir putin': 2,\n",
" \"'s 'lover\": 1,\n",
" 'brass eye': 1,\n",
" ' s outtakes': 1,\n",
" 'brutal tv comedy': 1,\n",
" 'threatens civilians': 1,\n",
" 'mariupol': 1,\n",
" \"'s spirit\": 1,\n",
" 'shell u-turn': 1,\n",
" 'cambo': 1,\n",
" 'green targets': 1,\n",
" 'st helens': 1,\n",
" 'dog attack': 1,\n",
" 'girl': 1,\n",
" 'bbc': 1,\n",
" 'playstation': 1,\n",
" \"'assassin 's\": 1,\n",
" 'creed': 1,\n",
" 'jade raymond': 1,\n",
" 'haven studios': 1,\n",
" 'nme': 1,\n",
" 'nintendo switch': 1,\n",
" 'folders •': 1,\n",
" 'eurogamer.net': 2,\n",
" 'fa': 1,\n",
" 'solution ”': 1,\n",
" 'liverpool': 1,\n",
" 'fan group blasts “ shambolic ”': 1,\n",
" 'wembley': 1,\n",
" 'anfield': 1,\n",
" 'manchester': 1,\n",
" 'live erik': 1,\n",
" 'hag': 1,\n",
" 'utd': 1,\n",
" 'manager updates': 1,\n",
" 'manchester evening': 1,\n",
" 'inflation': 1,\n",
" 'government borrowing': 1,\n",
" 'february': 1,\n",
" 'crude oil': 1,\n",
" ' business': 1,\n",
" 'kremlin': 1,\n",
" 'large-scale fraud': 1,\n",
" 'sky': 1,\n",
" 'natural gas': 1,\n",
" 'gazprom': 1,\n",
" 'retail unit': 1,\n",
" 'insider': 1,\n",
" 'zaghari-ratcliffe': 1,\n",
" 'hunt': 1,\n",
" 'iran': 1,\n",
" 'debt payment': 1}"
]
},
"execution_count": 22,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"w = {}\n",
"for x in all_titles:\n",
" for n in TextBlob(x).noun_phrases:\n",
" if n in w:\n",
" w[n].append(x)\n",
" else:\n",
" w[n]=[x]\n",
"{ x:len(w[x]) for x in w.keys()}"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Putem vedea că substantivele nu ne oferă grupuri tematice mari. Să înlocuim substantivele cu termeni mai generali obținuți din graful de concepte. Acest lucru va dura ceva timp, deoarece facem apeluri REST pentru fiecare expresie nominală.\n"
]
},
{
"cell_type": "code",
"execution_count": 23,
"metadata": {
"trusted": true
},
"outputs": [],
"source": [
"w = {}\n",
"for x in all_titles:\n",
" for noun in TextBlob(x).noun_phrases:\n",
" terms = query(noun.replace(' ','%20'))\n",
" for term in [u for u in terms.keys() if terms[u]>0.1]:\n",
" if term in w:\n",
" w[term].append(x)\n",
" else:\n",
" w[term]=[x]"
]
},
{
"cell_type": "code",
"execution_count": 24,
"metadata": {
"trusted": true
},
"outputs": [
{
"data": {
"text/plain": [
"{'city': 9,\n",
" 'brand': 4,\n",
" 'place': 9,\n",
" 'town': 4,\n",
" 'factor': 4,\n",
" 'film': 4,\n",
" 'nation': 11,\n",
" 'state': 5,\n",
" 'person': 4,\n",
" 'organization': 5,\n",
" 'publication': 10,\n",
" 'market': 5,\n",
" 'economy': 4,\n",
" 'company': 6,\n",
" 'newspaper': 6,\n",
" 'relationship': 6}"
]
},
"execution_count": 24,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"{ x:len(w[x]) for x in w.keys() if len(w[x])>3}"
]
},
{
"cell_type": "code",
"execution_count": 27,
"metadata": {
"trusted": true
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"\n",
"ECONOMY:\n",
"China searches for victims, flight recorders after first plane crash in 12 years - Reuters\n",
"Live updates: Russia stops talks with Japan over sanctions - The Associated Press - en Español\n",
"China plane crash live: Search for survivors continues as witness describes moment flight fell from sky - The Independent\n",
"UK prepares to nationalize Russia natural gas giant Gazprom's retail unit - Business Insider\n",
"\n",
"NATION:\n",
"Clear sign Putin considering using chemical weapons in Ukraine, claims President Biden - The Independent\n",
"Duchess of Cambridge swaps khaki jungle gear for Vampire's Wife dress on Belize trip - Daily Mail\n",
"China searches for victims, flight recorders after first plane crash in 12 years - Reuters\n",
"Live updates: Russia stops talks with Japan over sanctions - The Associated Press - en Español\n",
"Live updates: Russia stops talks with Japan over sanctions - The Associated Press - en Español\n",
"China plane crash live: Search for survivors continues as witness describes moment flight fell from sky - The Independent\n",
"UK and Republic of Ireland in line to host Euro 2028 after no one else bids - The Guardian\n",
"Friends beg Vladimir Putin's 'lover' to persuade him to end Ukraine invasion - The Mirror\n",
"Clear sign Putin considering using chemical weapons in Ukraine, claims President Biden - The Independent\n",
"UK prepares to nationalize Russia natural gas giant Gazprom's retail unit - Business Insider\n",
"Zaghari-Ratcliffe: Hunt calls for inquiry into delay over Iran debt payment - The Guardian\n",
"\n",
"PERSON:\n",
"Clear sign Putin considering using chemical weapons in Ukraine, claims President Biden - The Independent\n",
"Duchess of Cambridge swaps khaki jungle gear for Vampire's Wife dress on Belize trip - Daily Mail\n",
"Second superyacht linked to Russian oligarch Abramovich docks in Turkey - Reuters\n",
"Clear sign Putin considering using chemical weapons in Ukraine, claims President Biden - The Independent\n"
]
}
],
"source": [
"print('\\nECONOMY:\\n'+'\\n'.join(w['economy']))\n",
"print('\\nNATION:\\n'+'\\n'.join(w['nation']))\n",
"print('\\nPERSON:\\n'+'\\n'.join(w['person']))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"\n---\n\n**Declinare de responsabilitate**: \nAcest document a fost tradus folosind serviciul de traducere AI [Co-op Translator](https://github.com/Azure/co-op-translator). Deși ne străduim să asigurăm acuratețea, vă rugăm să fiți conștienți că traducerile automate pot conține erori sau inexactități. Documentul original în limba sa natală ar trebui considerat sursa autoritară. Pentru informații critice, se recomandă traducerea profesională realizată de un specialist uman. Nu ne asumăm responsabilitatea pentru eventualele neînțelegeri sau interpretări greșite care pot apărea din utilizarea acestei traduceri.\n"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3.7.4 64-bit (conda)",
"metadata": {
"interpreter": {
"hash": "86193a1ab0ba47eac1c69c1756090baa3b420b3eea7d4aafab8b85f8b312f0c5"
}
},
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.9.5"
},
"coopTranslator": {
"original_hash": "4087f998407d06ceb2947016ba4605d0",
"translation_date": "2025-08-29T23:57:00+00:00",
"source_file": "lessons/2-Symbolic/MSConceptGraph.ipynb",
"language_code": "ro"
}
},
"nbformat": 4,
"nbformat_minor": 2
}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -1,8 +1,8 @@
<!--
CO_OP_TRANSLATOR_METADATA:
{
"original_hash": "7336583e4630220c835335da640016db",
"translation_date": "2025-08-26T00:00:48+00:00",
"original_hash": "ba5d1eb353d20d3e7181066b3c424b99",
"translation_date": "2025-08-29T22:48:54+00:00",
"source_file": "lessons/3-NeuralNetworks/03-Perceptron/lab/README.md",
"language_code": "ro"
}
@ -13,7 +13,7 @@ Temă de laborator din [AI for Beginners Curriculum](https://github.com/microsof
## Sarcină
Folosind codul dezvoltat în această lecție pentru clasificarea binară a cifrelor scrise de mână din MNIST, creați un clasificator multi-clasă care să poată recunoaște orice cifră. Calculați acuratețea clasificării pe setul de date de antrenament și test, și afișați matricea de confuzie.
Folosind codul dezvoltat în această lecție pentru clasificarea binară a cifrelor scrise de mână din MNIST, creați un clasificator multi-clasă care să poată recunoaște orice cifră. Calculați acuratețea clasificării pe setul de date de antrenament și testare și afișați matricea de confuzie.
## Sugestii
@ -21,11 +21,13 @@ Folosind codul dezvoltat în această lecție pentru clasificarea binară a cifr
1. Antrenați 10 perceptroni diferiți pentru clasificarea binară (câte unul pentru fiecare cifră).
1. Definiți o funcție care să clasifice o cifră de intrare.
> **Sugestie**: Dacă combinăm greutățile tuturor celor 10 perceptroni într-o singură matrice, ar trebui să putem aplica toți cei 10 perceptroni asupra cifrelor de intrare printr-o singură înmulțire de matrice. Cifra cea mai probabilă poate fi apoi găsită doar aplicând operația `argmax` pe ieșire.
> **Sugestie**: Dacă combinăm greutățile tuturor celor 10 perceptroni într-o singură matrice, ar trebui să putem aplica toți cei 10 perceptroni cifrelor de intrare printr-o singură înmulțire matricială. Cifra cea mai probabilă poate fi apoi găsită doar aplicând operația `argmax` pe ieșire.
## Notebook de Pornire
Începeți laboratorul deschizând [PerceptronMultiClass.ipynb](../../../../../../lessons/3-NeuralNetworks/03-Perceptron/lab/PerceptronMultiClass.ipynb)
Începeți laboratorul deschizând [PerceptronMultiClass.ipynb](PerceptronMultiClass.ipynb)
---
**Declinare de responsabilitate**:
Acest document a fost tradus folosind serviciul de traducere AI [Co-op Translator](https://github.com/Azure/co-op-translator). Deși ne străduim să asigurăm acuratețea, vă rugăm să fiți conștienți că traducerile automate pot conține erori sau inexactități. Documentul original în limba sa natală ar trebui considerat sursa autoritară. Pentru informații critice, se recomandă traducerea profesională realizată de un specialist uman. Nu ne asumăm responsabilitatea pentru eventualele neînțelegeri sau interpretări greșite care pot apărea din utilizarea acestei traduceri.

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,183 @@
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Clasificarea Cifrelor MNIST cu propriul nostru Framework\n",
"\n",
"Temă de laborator din [Curriculum AI pentru Începători](https://github.com/microsoft/ai-for-beginners).\n",
"\n",
"### Citirea Setului de Date\n",
"\n",
"Acest cod descarcă setul de date dintr-un depozit de pe internet. De asemenea, poți copia manual setul de date din directorul `/data` al depozitului Curriculum AI.\n"
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {
"tags": []
},
"outputs": [
{
"name": "stderr",
"output_type": "stream",
"text": [
" % Total % Received % Xferd Average Speed Time Time Time Current\n",
" Dload Upload Total Spent Left Speed\n",
"\n",
" 0 0 0 0 0 0 0 0 --:--:-- --:--:-- --:--:-- 0\n",
"100 9.9M 100 9.9M 0 0 9.9M 0 0:00:01 --:--:-- 0:00:01 15.8M\n"
]
}
],
"source": [
"!rm *.pkl\n",
"!wget https://raw.githubusercontent.com/microsoft/AI-For-Beginners/main/data/mnist.pkl.gz\n",
"!gzip -d mnist.pkl.gz"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {},
"outputs": [],
"source": [
"import pickle\n",
"with open('mnist.pkl','rb') as f:\n",
" MNIST = pickle.load(f)"
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {},
"outputs": [],
"source": [
"labels = MNIST['Train']['Labels']\n",
"data = MNIST['Train']['Features']"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Să vedem care este forma datelor pe care le avem:\n"
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"(42000, 784)"
]
},
"execution_count": 5,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"data.shape"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Împărțirea Datelor\n",
"\n",
"Vom folosi Scikit Learn pentru a împărți datele între setul de antrenament și setul de testare:\n"
]
},
{
"cell_type": "code",
"execution_count": 6,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Train samples: 33600, test samples: 8400\n"
]
}
],
"source": [
"from sklearn.model_selection import train_test_split\n",
"\n",
"features_train, features_test, labels_train, labels_test = train_test_split(data,labels,test_size=0.2)\n",
"\n",
"print(f\"Train samples: {len(features_train)}, test samples: {len(features_test)}\")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Instrucțiuni\n",
"\n",
"1. Luați codul cadru din lecție și lipiți-l în acest notebook sau (și mai bine) într-un modul Python separat.\n",
"1. Definiți și antrenați un perceptron cu un singur strat, observând acuratețea de antrenare și validare pe parcursul antrenamentului.\n",
"1. Încercați să înțelegeți dacă a avut loc overfitting și ajustați parametrii stratului pentru a îmbunătăți acuratețea.\n",
"1. Repetați pașii anteriori pentru perceptroni cu 2 și 3 straturi. Încercați să experimentați cu diferite funcții de activare între straturi.\n",
"1. Încercați să răspundeți la următoarele întrebări:\n",
" - Funcția de activare dintre straturi afectează performanța rețelei?\n",
" - Avem nevoie de o rețea cu 2 sau 3 straturi pentru această sarcină?\n",
" - Ați întâmpinat probleme în antrenarea rețelei? Mai ales pe măsură ce numărul de straturi a crescut.\n",
" - Cum se comportă greutățile rețelei pe parcursul antrenamentului? Puteți să reprezentați grafic valoarea absolută maximă a greutăților în funcție de epocă pentru a înțelege relația.\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": []
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"\n---\n\n**Declinarea responsabilității**: \nAcest document a fost tradus folosind serviciul de traducere AI [Co-op Translator](https://github.com/Azure/co-op-translator). Deși depunem eforturi pentru a asigura acuratețea, vă rugăm să rețineți că traducerile automate pot conține erori sau inexactități. Documentul original în limba sa natală ar trebui considerat sursa autoritară. Pentru informații critice, se recomandă traducerea profesională realizată de un specialist uman. Nu ne asumăm răspunderea pentru eventualele neînțelegeri sau interpretări greșite care pot apărea din utilizarea acestei traduceri.\n"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3.7.4 64-bit (conda)",
"metadata": {
"interpreter": {
"hash": "86193a1ab0ba47eac1c69c1756090baa3b420b3eea7d4aafab8b85f8b312f0c5"
}
},
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.9.5"
},
"orig_nbformat": 2,
"coopTranslator": {
"original_hash": "6fa055f484eb5d6bdf41166a356d3abf",
"translation_date": "2025-08-30T00:06:27+00:00",
"source_file": "lessons/3-NeuralNetworks/04-OwnFramework/lab/MyFW_MNIST.ipynb",
"language_code": "ro"
}
},
"nbformat": 4,
"nbformat_minor": 2
}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,102 @@
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Obiectivul tău** va fi să folosești Optical Flow pentru a determina care părți ale videoclipului conțin mișcări sus/jos/stânga/dreapta.\n",
"\n",
"Începe prin obținerea cadrelor video, așa cum este descris în lecție:\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# Code here"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Acum, calculează cadrele de flux optic dens, așa cum este descris în prelegere, și convertește fluxul optic dens în coordonate polare:\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# Code here"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Construiește un histogramă al direcțiilor pentru fiecare cadru de flux optic. Un histogramă arată câți vectori se încadrează într-un anumit interval și ar trebui să separe diferitele direcții de mișcare din cadrul respectiv.\n",
"\n",
"> De asemenea, s-ar putea să dorești să elimini toți vectorii a căror magnitudine este sub un anumit prag. Acest lucru va elimina mișcările mici suplimentare din videoclip, cum ar fi mișcările ochilor și capului.\n",
"\n",
"Trasează histogramele pentru câteva dintre cadre.\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# Code here"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Privind histogramele, ar trebui să fie destul de simplu să determinați direcția mișcării. Trebuie să selectați acele bare care corespund direcțiilor sus/jos/stânga/dreapta și care sunt peste un anumit prag.\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# Code here"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Felicitări! Dacă ai urmat toți pașii de mai sus, ai finalizat laboratorul!\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"\n---\n\n**Declinare de responsabilitate**: \nAcest document a fost tradus folosind serviciul de traducere AI [Co-op Translator](https://github.com/Azure/co-op-translator). Deși ne străduim să asigurăm acuratețea, vă rugăm să fiți conștienți că traducerile automate pot conține erori sau inexactități. Documentul original în limba sa natală ar trebui considerat sursa autoritară. Pentru informații critice, se recomandă traducerea profesională realizată de un specialist uman. Nu ne asumăm responsabilitatea pentru eventualele neînțelegeri sau interpretări greșite care pot apărea din utilizarea acestei traduceri.\n"
]
}
],
"metadata": {
"language_info": {
"name": "python"
},
"orig_nbformat": 4,
"coopTranslator": {
"original_hash": "153d9e417e079bf62f8f693002d0deaf",
"translation_date": "2025-08-29T23:33:58+00:00",
"source_file": "lessons/4-ComputerVision/06-IntroCV/lab/MovementDetection.ipynb",
"language_code": "ro"
}
},
"nbformat": 4,
"nbformat_minor": 2
}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,577 @@
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Sarcina de clasificare a textului\n",
"\n",
"Așa cum am menționat, ne vom concentra pe o sarcină simplă de clasificare a textului bazată pe datasetul **AG_NEWS**, care presupune clasificarea titlurilor de știri în una dintre cele 4 categorii: Lume, Sport, Afaceri și Știință/Tehnologie.\n",
"\n",
"## Datasetul\n",
"\n",
"Acest dataset este integrat în modulul [`torchtext`](https://github.com/pytorch/text), așa că putem avea acces la el cu ușurință.\n"
]
},
{
"cell_type": "code",
"execution_count": 1,
"metadata": {},
"outputs": [],
"source": [
"import torch\n",
"import torchtext\n",
"import os\n",
"import collections\n",
"os.makedirs('./data',exist_ok=True)\n",
"train_dataset, test_dataset = torchtext.datasets.AG_NEWS(root='./data')\n",
"classes = ['World', 'Sports', 'Business', 'Sci/Tech']"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Aici, `train_dataset` și `test_dataset` conțin colecții care returnează perechi de etichetă (numărul clasei) și text, respectiv, de exemplu:\n"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"(3,\n",
" \"Wall St. Bears Claw Back Into the Black (Reuters) Reuters - Short-sellers, Wall Street's dwindling\\\\band of ultra-cynics, are seeing green again.\")"
]
},
"execution_count": 2,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"list(train_dataset)[0]"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Așadar, să afișăm primele 10 titluri noi din setul nostru de date:\n"
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"**Sci/Tech** -> Wall St. Bears Claw Back Into the Black (Reuters) Reuters - Short-sellers, Wall Street's dwindling\\band of ultra-cynics, are seeing green again.\n",
"**Sci/Tech** -> Carlyle Looks Toward Commercial Aerospace (Reuters) Reuters - Private investment firm Carlyle Group,\\which has a reputation for making well-timed and occasionally\\controversial plays in the defense industry, has quietly placed\\its bets on another part of the market.\n",
"**Sci/Tech** -> Oil and Economy Cloud Stocks' Outlook (Reuters) Reuters - Soaring crude prices plus worries\\about the economy and the outlook for earnings are expected to\\hang over the stock market next week during the depth of the\\summer doldrums.\n",
"**Sci/Tech** -> Iraq Halts Oil Exports from Main Southern Pipeline (Reuters) Reuters - Authorities have halted oil export\\flows from the main pipeline in southern Iraq after\\intelligence showed a rebel militia could strike\\infrastructure, an oil official said on Saturday.\n",
"**Sci/Tech** -> Oil prices soar to all-time record, posing new menace to US economy (AFP) AFP - Tearaway world oil prices, toppling records and straining wallets, present a new economic menace barely three months before the US presidential elections.\n"
]
}
],
"source": [
"for i,x in zip(range(5),train_dataset):\n",
" print(f\"**{classes[x[0]]}** -> {x[1]}\")\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Deoarece seturile de date sunt iteratoare, dacă dorim să folosim datele de mai multe ori, trebuie să le convertim într-o listă:\n"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {},
"outputs": [],
"source": [
"train_dataset, test_dataset = torchtext.datasets.AG_NEWS(root='./data')\n",
"train_dataset = list(train_dataset)\n",
"test_dataset = list(test_dataset)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Tokenizare\n",
"\n",
"Acum trebuie să transformăm textul în **numere** care pot fi reprezentate ca tensori. Dacă dorim o reprezentare la nivel de cuvinte, trebuie să facem două lucruri:\n",
"* utilizăm un **tokenizator** pentru a împărți textul în **tokeni**\n",
"* construim un **vocabular** al acelor tokeni.\n"
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"['he', 'said', 'hello']"
]
},
"execution_count": 4,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"tokenizer = torchtext.data.utils.get_tokenizer('basic_english')\n",
"tokenizer('He said: hello')"
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {},
"outputs": [],
"source": [
"counter = collections.Counter()\n",
"for (label, line) in train_dataset:\n",
" counter.update(tokenizer(line))\n",
"vocab = torchtext.vocab.vocab(counter, min_freq=1)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Folosind vocabularul, putem codifica cu ușurință șirul nostru tokenizat într-un set de numere:\n"
]
},
{
"cell_type": "code",
"execution_count": 19,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Vocab size if 95810\n"
]
},
{
"data": {
"text/plain": [
"[599, 3279, 97, 1220, 329, 225, 7368]"
]
},
"execution_count": 19,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"vocab_size = len(vocab)\n",
"print(f\"Vocab size if {vocab_size}\")\n",
"\n",
"stoi = vocab.get_stoi() # dict to convert tokens to indices\n",
"\n",
"def encode(x):\n",
" return [stoi[s] for s in tokenizer(x)]\n",
"\n",
"encode('I love to play with my words')"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Reprezentarea textului prin metoda Bag of Words\n",
"\n",
"Deoarece cuvintele transmit semnificație, uneori putem înțelege sensul unui text doar analizând cuvintele individuale, indiferent de ordinea lor în propoziție. De exemplu, atunci când clasificăm știri, cuvinte precum *vreme*, *zăpadă* sunt probabil să indice *prognoza meteo*, în timp ce cuvinte precum *acțiuni*, *dolar* ar putea sugera *știri financiare*.\n",
"\n",
"Reprezentarea vectorială **Bag of Words** (BoW) este cea mai utilizată metodă tradițională de reprezentare vectorială. Fiecare cuvânt este asociat unui index vectorial, iar elementul vectorului conține numărul de apariții ale unui cuvânt într-un document dat.\n",
"\n",
"![Imagine care arată cum este reprezentată în memorie metoda bag of words.](../../../../../translated_images/bag-of-words-example.606fc1738f1d7ba98a9d693e3bcd706c6e83fa7bf8221e6e90d1a206d82f2ea4.ro.png) \n",
"\n",
"> **Note**: Poți să te gândești la BoW și ca la o sumă a tuturor vectorilor one-hot-encoded pentru cuvintele individuale din text.\n",
"\n",
"Mai jos este un exemplu despre cum să generezi o reprezentare bag of words folosind biblioteca python Scikit Learn:\n"
]
},
{
"cell_type": "code",
"execution_count": 7,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"array([[1, 1, 0, 2, 0, 0, 0, 0, 0]], dtype=int64)"
]
},
"execution_count": 7,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"from sklearn.feature_extraction.text import CountVectorizer\n",
"vectorizer = CountVectorizer()\n",
"corpus = [\n",
" 'I like hot dogs.',\n",
" 'The dog ran fast.',\n",
" 'Its hot outside.',\n",
" ]\n",
"vectorizer.fit_transform(corpus)\n",
"vectorizer.transform(['My dog likes hot dogs on a hot day.']).toarray()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Pentru a calcula vectorul bag-of-words din reprezentarea vectorială a datasetului nostru AG_NEWS, putem folosi următoarea funcție:\n"
]
},
{
"cell_type": "code",
"execution_count": 20,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"tensor([2., 1., 2., ..., 0., 0., 0.])\n"
]
}
],
"source": [
"vocab_size = len(vocab)\n",
"\n",
"def to_bow(text,bow_vocab_size=vocab_size):\n",
" res = torch.zeros(bow_vocab_size,dtype=torch.float32)\n",
" for i in encode(text):\n",
" if i<bow_vocab_size:\n",
" res[i] += 1\n",
" return res\n",
"\n",
"print(to_bow(train_dataset[0][1]))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"> **Notă:** Aici folosim variabila globală `vocab_size` pentru a specifica dimensiunea implicită a vocabularului. Deoarece dimensiunea vocabularului este adesea destul de mare, putem limita dimensiunea vocabularului la cele mai frecvente cuvinte. Încercați să reduceți valoarea `vocab_size` și să rulați codul de mai jos, și observați cum afectează acuratețea. Ar trebui să vă așteptați la o scădere a acurateței, dar nu dramatică, în schimbul unei performanțe mai ridicate.\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Antrenarea clasificatorului BoW\n",
"\n",
"Acum că am învățat cum să construim reprezentarea Bag-of-Words a textului nostru, să antrenăm un clasificator pe baza acesteia. Mai întâi, trebuie să convertim setul nostru de date pentru antrenare astfel încât toate reprezentările vectoriale poziționale să fie transformate în reprezentări Bag-of-Words. Acest lucru poate fi realizat prin transmiterea funcției `bowify` ca parametru `collate_fn` la `DataLoader` standard din torch:\n"
]
},
{
"cell_type": "code",
"execution_count": 21,
"metadata": {},
"outputs": [],
"source": [
"from torch.utils.data import DataLoader\n",
"import numpy as np \n",
"\n",
"# this collate function gets list of batch_size tuples, and needs to \n",
"# return a pair of label-feature tensors for the whole minibatch\n",
"def bowify(b):\n",
" return (\n",
" torch.LongTensor([t[0]-1 for t in b]),\n",
" torch.stack([to_bow(t[1]) for t in b])\n",
" )\n",
"\n",
"train_loader = DataLoader(train_dataset, batch_size=16, collate_fn=bowify, shuffle=True)\n",
"test_loader = DataLoader(test_dataset, batch_size=16, collate_fn=bowify, shuffle=True)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Acum să definim o rețea neuronală clasificatoare simplă care conține un singur strat liniar. Dimensiunea vectorului de intrare este egală cu `vocab_size`, iar dimensiunea de ieșire corespunde numărului de clase (4). Deoarece rezolvăm o sarcină de clasificare, funcția de activare finală este `LogSoftmax()`.\n"
]
},
{
"cell_type": "code",
"execution_count": 22,
"metadata": {},
"outputs": [],
"source": [
"net = torch.nn.Sequential(torch.nn.Linear(vocab_size,4),torch.nn.LogSoftmax(dim=1))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Acum vom defini bucla standard de antrenament PyTorch. Deoarece setul nostru de date este destul de mare, pentru scopul nostru educativ vom antrena doar pentru o epocă, și uneori chiar pentru mai puțin de o epocă (specificarea parametrului `epoch_size` ne permite să limităm antrenamentul). De asemenea, vom raporta acuratețea acumulată a antrenamentului în timpul procesului; frecvența raportării este specificată folosind parametrul `report_freq`.\n"
]
},
{
"cell_type": "code",
"execution_count": 24,
"metadata": {},
"outputs": [],
"source": [
"def train_epoch(net,dataloader,lr=0.01,optimizer=None,loss_fn = torch.nn.NLLLoss(),epoch_size=None, report_freq=200):\n",
" optimizer = optimizer or torch.optim.Adam(net.parameters(),lr=lr)\n",
" net.train()\n",
" total_loss,acc,count,i = 0,0,0,0\n",
" for labels,features in dataloader:\n",
" optimizer.zero_grad()\n",
" out = net(features)\n",
" loss = loss_fn(out,labels) #cross_entropy(out,labels)\n",
" loss.backward()\n",
" optimizer.step()\n",
" total_loss+=loss\n",
" _,predicted = torch.max(out,1)\n",
" acc+=(predicted==labels).sum()\n",
" count+=len(labels)\n",
" i+=1\n",
" if i%report_freq==0:\n",
" print(f\"{count}: acc={acc.item()/count}\")\n",
" if epoch_size and count>epoch_size:\n",
" break\n",
" return total_loss.item()/count, acc.item()/count"
]
},
{
"cell_type": "code",
"execution_count": 25,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"3200: acc=0.8028125\n",
"6400: acc=0.8371875\n",
"9600: acc=0.8534375\n",
"12800: acc=0.85765625\n"
]
},
{
"data": {
"text/plain": [
"(0.026090790722161722, 0.8620069296375267)"
]
},
"execution_count": 25,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"train_epoch(net,train_loader,epoch_size=15000)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## BiGrame, TriGrame și N-Grame\n",
"\n",
"O limitare a abordării sacului de cuvinte este că unele cuvinte fac parte din expresii formate din mai multe cuvinte. De exemplu, cuvântul „hot dog” are un sens complet diferit față de cuvintele „hot” și „dog” în alte contexte. Dacă reprezentăm întotdeauna cuvintele „hot” și „dog” prin aceleași vectori, acest lucru poate deruta modelul nostru.\n",
"\n",
"Pentru a rezolva această problemă, **reprezentările N-gram** sunt adesea utilizate în metodele de clasificare a documentelor, unde frecvența fiecărui cuvânt, bi-cuvânt sau tri-cuvânt este o caracteristică utilă pentru antrenarea clasificatorilor. În reprezentarea bigramelor, de exemplu, vom adăuga toate perechile de cuvinte în vocabular, pe lângă cuvintele originale.\n",
"\n",
"Mai jos este un exemplu despre cum să generăm o reprezentare de tip sac de cuvinte cu bigrame folosind Scikit Learn:\n"
]
},
{
"cell_type": "code",
"execution_count": 26,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Vocabulary:\n",
" {'i': 7, 'like': 11, 'hot': 4, 'dogs': 2, 'i like': 8, 'like hot': 12, 'hot dogs': 5, 'the': 16, 'dog': 0, 'ran': 14, 'fast': 3, 'the dog': 17, 'dog ran': 1, 'ran fast': 15, 'its': 9, 'outside': 13, 'its hot': 10, 'hot outside': 6}\n"
]
},
{
"data": {
"text/plain": [
"array([[1, 0, 1, 0, 2, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]],\n",
" dtype=int64)"
]
},
"execution_count": 26,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"bigram_vectorizer = CountVectorizer(ngram_range=(1, 2), token_pattern=r'\\b\\w+\\b', min_df=1)\n",
"corpus = [\n",
" 'I like hot dogs.',\n",
" 'The dog ran fast.',\n",
" 'Its hot outside.',\n",
" ]\n",
"bigram_vectorizer.fit_transform(corpus)\n",
"print(\"Vocabulary:\\n\",bigram_vectorizer.vocabulary_)\n",
"bigram_vectorizer.transform(['My dog likes hot dogs on a hot day.']).toarray()\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Principalul dezavantaj al abordării N-gram este că dimensiunea vocabularului începe să crească extrem de rapid. În practică, trebuie să combinăm reprezentarea N-gram cu unele tehnici de reducere a dimensionalității, cum ar fi *embeddings*, pe care le vom discuta în unitatea următoare.\n",
"\n",
"Pentru a utiliza reprezentarea N-gram în setul nostru de date **AG News**, trebuie să construim un vocabular special de tip ngram:\n"
]
},
{
"cell_type": "code",
"execution_count": 27,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Bigram vocabulary length = 1308842\n"
]
}
],
"source": [
"counter = collections.Counter()\n",
"for (label, line) in train_dataset:\n",
" l = tokenizer(line)\n",
" counter.update(torchtext.data.utils.ngrams_iterator(l,ngrams=2))\n",
" \n",
"bi_vocab = torchtext.vocab.vocab(counter, min_freq=1)\n",
"\n",
"print(\"Bigram vocabulary length = \",len(bi_vocab))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Am putea folosi același cod de mai sus pentru a antrena clasificatorul, totuși, acest lucru ar fi foarte ineficient din punct de vedere al memoriei. În unitatea următoare, vom antrena un clasificator bigram folosind embeddings.\n",
"\n",
"> **Notă:** Poți păstra doar acele ngrame care apar în text de mai multe ori decât numărul specificat. Acest lucru va asigura că bigramele rare vor fi omise și va reduce semnificativ dimensiunea vocabularului. Pentru a face acest lucru, setează parametrul `min_freq` la o valoare mai mare și observă cum se schimbă lungimea vocabularului.\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Frecvența Termenilor și Frecvența Inversă a Documentelor (TF-IDF)\n",
"\n",
"În reprezentarea BoW, aparițiile cuvintelor sunt ponderate uniform, indiferent de cuvântul în sine. Totuși, este evident că anumite cuvinte frecvente, precum *un*, *în*, etc., sunt mult mai puțin importante pentru clasificare decât termenii specializați. De fapt, în majoritatea sarcinilor NLP, unele cuvinte sunt mai relevante decât altele.\n",
"\n",
"**TF-IDF** este abrevierea pentru **frecvența termenilor frecvența inversă a documentelor**. Este o variație a modelului bag of words, unde, în loc de o valoare binară 0/1 care indică apariția unui cuvânt într-un document, se folosește o valoare în virgulă mobilă, care este legată de frecvența apariției cuvântului în corpus.\n",
"\n",
"Mai formal, greutatea $w_{ij}$ a unui cuvânt $i$ în documentul $j$ este definită astfel:\n",
"$$\n",
"w_{ij} = tf_{ij}\\times\\log({N\\over df_i})\n",
"$$\n",
"unde\n",
"* $tf_{ij}$ este numărul de apariții ale lui $i$ în $j$, adică valoarea BoW pe care am văzut-o anterior\n",
"* $N$ este numărul de documente din colecție\n",
"* $df_i$ este numărul de documente care conțin cuvântul $i$ în întreaga colecție\n",
"\n",
"Valoarea TF-IDF $w_{ij}$ crește proporțional cu numărul de apariții ale unui cuvânt într-un document și este ajustată în funcție de numărul de documente din corpus care conțin acel cuvânt, ceea ce ajută la compensarea faptului că unele cuvinte apar mai frecvent decât altele. De exemplu, dacă un cuvânt apare în *fiecare* document din colecție, $df_i=N$, și $w_{ij}=0$, iar acești termeni vor fi complet ignorați.\n",
"\n",
"Poți crea cu ușurință o vectorizare TF-IDF a textului folosind Scikit Learn:\n"
]
},
{
"cell_type": "code",
"execution_count": 28,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"array([[0.43381609, 0. , 0.43381609, 0. , 0.65985664,\n",
" 0.43381609, 0. , 0. , 0. , 0. ,\n",
" 0. , 0. , 0. , 0. , 0. ,\n",
" 0. ]])"
]
},
"execution_count": 28,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"from sklearn.feature_extraction.text import TfidfVectorizer\n",
"vectorizer = TfidfVectorizer(ngram_range=(1,2))\n",
"vectorizer.fit_transform(corpus)\n",
"vectorizer.transform(['My dog likes hot dogs on a hot day.']).toarray()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Concluzie\n",
"\n",
"Totuși, deși reprezentările TF-IDF oferă o pondere bazată pe frecvența diferitelor cuvinte, ele nu pot reprezenta sensul sau ordinea. Așa cum a spus faimosul lingvist J. R. Firth în 1935: „Sensul complet al unui cuvânt este întotdeauna contextual, iar niciun studiu al sensului în afara contextului nu poate fi luat în serios.”. Vom învăța mai târziu în curs cum să capturăm informațiile contextuale din text folosind modelarea limbajului.\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"\n---\n\n**Declinare de responsabilitate**: \nAcest document a fost tradus folosind serviciul de traducere AI [Co-op Translator](https://github.com/Azure/co-op-translator). Deși ne străduim să asigurăm acuratețea, vă rugăm să fiți conștienți că traducerile automate pot conține erori sau inexactități. Documentul original în limba sa natală ar trebui considerat sursa autoritară. Pentru informații critice, se recomandă traducerea profesională realizată de un specialist uman. Nu ne asumăm responsabilitatea pentru eventualele neînțelegeri sau interpretări greșite care pot apărea din utilizarea acestei traduceri.\n"
]
}
],
"metadata": {
"interpreter": {
"hash": "16af2a8bbb083ea23e5e41c7f5787656b2ce26968575d8763f2c4b17f9cd711f"
},
"kernelspec": {
"display_name": "Python 3.8.12 ('py38')",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.8.12"
},
"coopTranslator": {
"original_hash": "7b9040985e748e4e2d4c689892456ad7",
"translation_date": "2025-08-30T01:12:11+00:00",
"source_file": "lessons/5-NLP/13-TextRep/TextRepresentationPyTorch.ipynb",
"language_code": "ro"
}
},
"nbformat": 4,
"nbformat_minor": 2
}

View File

@ -0,0 +1,647 @@
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Sarcina de clasificare a textului\n",
"\n",
"În acest modul, vom începe cu o sarcină simplă de clasificare a textului bazată pe setul de date **[AG_NEWS](http://www.di.unipi.it/~gulli/AG_corpus_of_news_articles.html)**: vom clasifica titlurile știrilor în una dintre cele 4 categorii: Lume, Sport, Afaceri și Știință/Tehnologie.\n",
"\n",
"## Setul de date\n",
"\n",
"Pentru a încărca setul de date, vom folosi API-ul **[TensorFlow Datasets](https://www.tensorflow.org/datasets)**.\n"
]
},
{
"cell_type": "code",
"execution_count": 1,
"metadata": {},
"outputs": [],
"source": [
"import tensorflow as tf\n",
"from tensorflow import keras\n",
"import tensorflow_datasets as tfds\n",
"\n",
"# In this tutorial, we will be training a lot of models. In order to use GPU memory cautiously,\n",
"# we will set tensorflow option to grow GPU memory allocation when required.\n",
"physical_devices = tf.config.list_physical_devices('GPU') \n",
"if len(physical_devices)>0:\n",
" tf.config.experimental.set_memory_growth(physical_devices[0], True)\n",
"\n",
"dataset = tfds.load('ag_news_subset')"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Putem acum să accesăm părțile de antrenament și testare ale setului de date folosind `dataset['train']` și `dataset['test']` respectiv:\n"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Length of train dataset = 120000\n",
"Length of test dataset = 7600\n"
]
}
],
"source": [
"ds_train = dataset['train']\n",
"ds_test = dataset['test']\n",
"\n",
"print(f\"Length of train dataset = {len(ds_train)}\")\n",
"print(f\"Length of test dataset = {len(ds_test)}\")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Să imprimăm primele 10 titluri noi din setul nostru de date:\n"
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"3 (Sci/Tech) -> b'AMD Debuts Dual-Core Opteron Processor' b'AMD #39;s new dual-core Opteron chip is designed mainly for corporate computing applications, including databases, Web services, and financial transactions.'\n",
"1 (Sports) -> b\"Wood's Suspension Upheld (Reuters)\" b'Reuters - Major League Baseball\\\\Monday announced a decision on the appeal filed by Chicago Cubs\\\\pitcher Kerry Wood regarding a suspension stemming from an\\\\incident earlier this season.'\n",
"2 (Business) -> b'Bush reform may have blue states seeing red' b'President Bush #39;s quot;revenue-neutral quot; tax reform needs losers to balance its winners, and people claiming the federal deduction for state and local taxes may be in administration planners #39; sights, news reports say.'\n",
"3 (Sci/Tech) -> b\"'Halt science decline in schools'\" b'Britain will run out of leading scientists unless science education is improved, says Professor Colin Pillinger.'\n",
"1 (Sports) -> b'Gerrard leaves practice' b'London, England (Sports Network) - England midfielder Steven Gerrard injured his groin late in Thursday #39;s training session, but is hopeful he will be ready for Saturday #39;s World Cup qualifier against Austria.'\n"
]
}
],
"source": [
"classes = ['World', 'Sports', 'Business', 'Sci/Tech']\n",
"\n",
"for i,x in zip(range(5),ds_train):\n",
" print(f\"{x['label']} ({classes[x['label']]}) -> {x['title']} {x['description']}\")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Vectorizarea textului\n",
"\n",
"Acum trebuie să convertim textul în **numere** care pot fi reprezentate ca tensori. Dacă dorim o reprezentare la nivel de cuvinte, trebuie să facem două lucruri:\n",
"\n",
"* Folosim un **tokenizator** pentru a împărți textul în **tokeni**.\n",
"* Construim un **vocabular** din acei tokeni.\n",
"\n",
"### Limitarea dimensiunii vocabularului\n",
"\n",
"În exemplul dataset-ului AG News, dimensiunea vocabularului este destul de mare, mai mult de 100k cuvinte. În general, nu avem nevoie de cuvinte care apar rar în text — doar câteva propoziții le vor conține, iar modelul nu va învăța din ele. Astfel, are sens să limităm dimensiunea vocabularului la un număr mai mic, prin transmiterea unui argument constructorului vectorizatorului:\n",
"\n",
"Ambele etape pot fi gestionate folosind stratul **TextVectorization**. Să instanțiem obiectul vectorizator și apoi să apelăm metoda `adapt` pentru a parcurge tot textul și a construi un vocabular:\n"
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {},
"outputs": [],
"source": [
"vocab_size = 50000\n",
"vectorizer = keras.layers.experimental.preprocessing.TextVectorization(max_tokens=vocab_size)\n",
"vectorizer.adapt(ds_train.take(500).map(lambda x: x['title']+' '+x['description']))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"> **Notă** că folosim doar un subset al întregului set de date pentru a construi un vocabular. Facem acest lucru pentru a accelera timpul de execuție și pentru a nu te ține în așteptare. Totuși, ne asumăm riscul ca unele dintre cuvintele din întregul set de date să nu fie incluse în vocabular și să fie ignorate în timpul antrenării. Astfel, utilizarea dimensiunii întregului vocabular și parcurgerea întregului set de date în timpul `adapt` ar trebui să crească acuratețea finală, dar nu semnificativ.\n",
"\n",
"Acum putem accesa vocabularul actual:\n"
]
},
{
"cell_type": "code",
"execution_count": 6,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"['', '[UNK]', 'the', 'to', 'a', 'in', 'of', 'and', 'on', 'for']\n",
"Length of vocabulary: 5335\n"
]
}
],
"source": [
"vocab = vectorizer.get_vocabulary()\n",
"vocab_size = len(vocab)\n",
"print(vocab[:10])\n",
"print(f\"Length of vocabulary: {vocab_size}\")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Folosind vectorizatorul, putem codifica cu ușurință orice text într-un set de numere:\n"
]
},
{
"cell_type": "code",
"execution_count": 7,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"<tf.Tensor: shape=(7,), dtype=int64, numpy=array([ 112, 3695, 3, 304, 11, 1041, 1], dtype=int64)>"
]
},
"execution_count": 7,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"vectorizer('I love to play with my words')"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Reprezentarea textului prin metoda Bag-of-words\n",
"\n",
"Deoarece cuvintele transmit semnificație, uneori putem înțelege sensul unui text doar analizând cuvintele individuale, indiferent de ordinea lor în propoziție. De exemplu, atunci când clasificăm știri, cuvinte precum *vreme* și *zăpadă* sunt susceptibile să indice *prognoza meteo*, în timp ce cuvinte precum *acțiuni* și *dolar* ar sugera *știri financiare*.\n",
"\n",
"Reprezentarea vectorială **Bag-of-words** (BoW) este cea mai simplă de înțeles dintre reprezentările vectoriale tradiționale. Fiecare cuvânt este asociat unui index vectorial, iar un element al vectorului conține numărul de apariții ale fiecărui cuvânt într-un document dat.\n",
"\n",
"![Imagine care arată cum este reprezentată în memorie o reprezentare vectorială bag-of-words.](../../../../../translated_images/bag-of-words-example.606fc1738f1d7ba98a9d693e3bcd706c6e83fa7bf8221e6e90d1a206d82f2ea4.ro.png) \n",
"\n",
"> **Note**: Poți considera BoW și ca o sumă a tuturor vectorilor one-hot-encoded pentru cuvintele individuale din text.\n",
"\n",
"Mai jos este un exemplu despre cum să generezi o reprezentare bag-of-words folosind biblioteca python Scikit Learn:\n"
]
},
{
"cell_type": "code",
"execution_count": 8,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"array([[1, 1, 0, 2, 0, 0, 0, 0, 0]], dtype=int64)"
]
},
"execution_count": 8,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"from sklearn.feature_extraction.text import CountVectorizer\n",
"sc_vectorizer = CountVectorizer()\n",
"corpus = [\n",
" 'I like hot dogs.',\n",
" 'The dog ran fast.',\n",
" 'Its hot outside.',\n",
" ]\n",
"sc_vectorizer.fit_transform(corpus)\n",
"sc_vectorizer.transform(['My dog likes hot dogs on a hot day.']).toarray()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Putem folosi, de asemenea, vectorizatorul Keras pe care l-am definit mai sus, transformând fiecare număr de cuvânt într-o codificare one-hot și adunând toți acei vectori:\n"
]
},
{
"cell_type": "code",
"execution_count": 9,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"array([0., 5., 0., ..., 0., 0., 0.], dtype=float32)"
]
},
"execution_count": 9,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"def to_bow(text):\n",
" return tf.reduce_sum(tf.one_hot(vectorizer(text),vocab_size),axis=0)\n",
"\n",
"to_bow('My dog likes hot dogs on a hot day.').numpy()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"> **Notă**: S-ar putea să fiți surprins că rezultatul diferă de exemplul anterior. Motivul este că, în exemplul Keras, lungimea vectorului corespunde dimensiunii vocabularului, care a fost construit din întregul set de date AG News, în timp ce în exemplul Scikit Learn am construit vocabularul din textul eșantion pe loc.\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Antrenarea clasificatorului BoW\n",
"\n",
"Acum că am învățat cum să construim reprezentarea bag-of-words a textului nostru, să antrenăm un clasificator care o folosește. Mai întâi, trebuie să convertim setul nostru de date într-o reprezentare bag-of-words. Acest lucru poate fi realizat folosind funcția `map` în următorul mod:\n"
]
},
{
"cell_type": "code",
"execution_count": 11,
"metadata": {},
"outputs": [],
"source": [
"batch_size = 128\n",
"\n",
"ds_train_bow = ds_train.map(lambda x: (to_bow(x['title']+x['description']),x['label'])).batch(batch_size)\n",
"ds_test_bow = ds_test.map(lambda x: (to_bow(x['title']+x['description']),x['label'])).batch(batch_size)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Acum să definim o rețea neuronală clasificatoare simplă care conține un singur strat liniar. Dimensiunea de intrare este `vocab_size`, iar dimensiunea de ieșire corespunde numărului de clase (4). Deoarece rezolvăm o sarcină de clasificare, funcția de activare finală este **softmax**:\n"
]
},
{
"cell_type": "code",
"execution_count": 12,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"938/938 [==============================] - 66s 70ms/step - loss: 0.6144 - acc: 0.8427 - val_loss: 0.4416 - val_acc: 0.8697\n"
]
},
{
"data": {
"text/plain": [
"<keras.callbacks.History at 0x20c70a947f0>"
]
},
"execution_count": 12,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"model = keras.models.Sequential([\n",
" keras.layers.Dense(4,activation='softmax',input_shape=(vocab_size,))\n",
"])\n",
"model.compile(loss='sparse_categorical_crossentropy',optimizer='adam',metrics=['acc'])\n",
"model.fit(ds_train_bow,validation_data=ds_test_bow)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Deoarece avem 4 clase, o acuratețe de peste 80% este un rezultat bun.\n",
"\n",
"## Antrenarea unui clasificator ca o singură rețea\n",
"\n",
"Pentru că vectorizatorul este, de asemenea, un strat Keras, putem defini o rețea care îl include și o putem antrena cap-coadă. În acest fel, nu mai este nevoie să vectorizăm setul de date folosind `map`, ci putem pur și simplu să trecem setul de date original la intrarea rețelei.\n",
"\n",
"> **Note**: Totuși, va trebui să aplicăm mapări setului de date pentru a converti câmpurile din dicționare (cum ar fi `title`, `description` și `label`) în tupluri. Cu toate acestea, atunci când încărcăm datele de pe disc, putem construi un set de date cu structura necesară de la bun început.\n"
]
},
{
"cell_type": "code",
"execution_count": 13,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Model: \"model\"\n",
"_________________________________________________________________\n",
" Layer (type) Output Shape Param # \n",
"=================================================================\n",
" input_1 (InputLayer) [(None, 1)] 0 \n",
" \n",
" text_vectorization (TextVec (None, None) 0 \n",
" torization) \n",
" \n",
" tf.one_hot (TFOpLambda) (None, None, 5335) 0 \n",
" \n",
" tf.math.reduce_sum (TFOpLam (None, 5335) 0 \n",
" bda) \n",
" \n",
" dense_2 (Dense) (None, 4) 21344 \n",
" \n",
"=================================================================\n",
"Total params: 21,344\n",
"Trainable params: 21,344\n",
"Non-trainable params: 0\n",
"_________________________________________________________________\n",
"938/938 [==============================] - 73s 77ms/step - loss: 0.6057 - acc: 0.8414 - val_loss: 0.4202 - val_acc: 0.8736\n"
]
},
{
"data": {
"text/plain": [
"<keras.callbacks.History at 0x20c721521f0>"
]
},
"execution_count": 13,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"def extract_text(x):\n",
" return x['title']+' '+x['description']\n",
"\n",
"def tupelize(x):\n",
" return (extract_text(x),x['label'])\n",
"\n",
"inp = keras.Input(shape=(1,),dtype=tf.string)\n",
"x = vectorizer(inp)\n",
"x = tf.reduce_sum(tf.one_hot(x,vocab_size),axis=1)\n",
"out = keras.layers.Dense(4,activation='softmax')(x)\n",
"model = keras.models.Model(inp,out)\n",
"model.summary()\n",
"\n",
"model.compile(loss='sparse_categorical_crossentropy',optimizer='adam',metrics=['acc'])\n",
"model.fit(ds_train.map(tupelize).batch(batch_size),validation_data=ds_test.map(tupelize).batch(batch_size))\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Bigrame, trigrame și n-grame\n",
"\n",
"O limitare a abordării sacului de cuvinte este că unele cuvinte fac parte din expresii formate din mai multe cuvinte; de exemplu, cuvântul „hot dog” are un sens complet diferit față de cuvintele „hot” și „dog” în alte contexte. Dacă reprezentăm cuvintele „hot” și „dog” mereu folosind aceleași vectori, acest lucru poate deruta modelul nostru.\n",
"\n",
"Pentru a rezolva această problemă, **reprezentările n-gram** sunt adesea utilizate în metodele de clasificare a documentelor, unde frecvența fiecărui cuvânt, pereche de cuvinte sau grup de trei cuvinte este o caracteristică utilă pentru antrenarea clasificatorilor. În reprezentările bigram, de exemplu, vom adăuga toate perechile de cuvinte în vocabular, pe lângă cuvintele originale.\n",
"\n",
"Mai jos este un exemplu despre cum să generăm o reprezentare bigram a sacului de cuvinte folosind Scikit Learn:\n"
]
},
{
"cell_type": "code",
"execution_count": 14,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Vocabulary:\n",
" {'i': 7, 'like': 11, 'hot': 4, 'dogs': 2, 'i like': 8, 'like hot': 12, 'hot dogs': 5, 'the': 16, 'dog': 0, 'ran': 14, 'fast': 3, 'the dog': 17, 'dog ran': 1, 'ran fast': 15, 'its': 9, 'outside': 13, 'its hot': 10, 'hot outside': 6}\n"
]
},
{
"data": {
"text/plain": [
"array([[1, 0, 1, 0, 2, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]],\n",
" dtype=int64)"
]
},
"execution_count": 14,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"bigram_vectorizer = CountVectorizer(ngram_range=(1, 2), token_pattern=r'\\b\\w+\\b', min_df=1)\n",
"corpus = [\n",
" 'I like hot dogs.',\n",
" 'The dog ran fast.',\n",
" 'Its hot outside.',\n",
" ]\n",
"bigram_vectorizer.fit_transform(corpus)\n",
"print(\"Vocabulary:\\n\",bigram_vectorizer.vocabulary_)\n",
"bigram_vectorizer.transform(['My dog likes hot dogs on a hot day.']).toarray()\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Principalul dezavantaj al abordării n-gram este că dimensiunea vocabularului începe să crească extrem de rapid. În practică, trebuie să combinăm reprezentarea n-gram cu o tehnică de reducere a dimensionalității, cum ar fi *embeddings*, pe care o vom discuta în unitatea următoare.\n",
"\n",
"Pentru a utiliza o reprezentare n-gram în setul nostru de date **AG News**, trebuie să transmitem parametrul `ngrams` constructorului nostru `TextVectorization`. Lungimea unui vocabular de bigram este **semnificativ mai mare**, în cazul nostru depășește 1,3 milioane de tokeni! Astfel, are sens să limităm și tokenii bigram la un număr rezonabil.\n",
"\n",
"Am putea folosi același cod ca mai sus pentru a antrena clasificatorul, însă ar fi foarte ineficient din punct de vedere al memoriei. În unitatea următoare, vom antrena clasificatorul bigram folosind embeddings. Între timp, poți experimenta cu antrenarea clasificatorului bigram în acest notebook și să vezi dacă poți obține o acuratețe mai mare.\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Calcularea automată a vectorilor BoW\n",
"\n",
"În exemplul de mai sus, am calculat vectorii BoW manual, prin însumarea codificărilor one-hot ale cuvintelor individuale. Totuși, cea mai recentă versiune a TensorFlow ne permite să calculăm vectorii BoW automat, prin transmiterea parametrului `output_mode='count` constructorului vectorizatorului. Acest lucru face definirea și antrenarea modelului nostru semnificativ mai ușoare:\n"
]
},
{
"cell_type": "code",
"execution_count": 15,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Training vectorizer\n",
"938/938 [==============================] - 7s 7ms/step - loss: 0.5929 - acc: 0.8486 - val_loss: 0.4168 - val_acc: 0.8772\n"
]
},
{
"data": {
"text/plain": [
"<keras.callbacks.History at 0x20c725217c0>"
]
},
"execution_count": 15,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"model = keras.models.Sequential([\n",
" keras.layers.experimental.preprocessing.TextVectorization(max_tokens=vocab_size,output_mode='count'),\n",
" keras.layers.Dense(4,input_shape=(vocab_size,), activation='softmax')\n",
"])\n",
"print(\"Training vectorizer\")\n",
"model.layers[0].adapt(ds_train.take(500).map(extract_text))\n",
"model.compile(loss='sparse_categorical_crossentropy',optimizer='adam',metrics=['acc'])\n",
"model.fit(ds_train.map(tupelize).batch(batch_size),validation_data=ds_test.map(tupelize).batch(batch_size))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Frecvența termenilor - frecvența inversă a documentelor (TF-IDF)\n",
"\n",
"În reprezentarea BoW, aparițiile cuvintelor sunt ponderate folosind aceeași tehnică, indiferent de cuvântul în sine. Totuși, este evident că cuvinte frecvente precum *a* și *în* sunt mult mai puțin importante pentru clasificare decât termenii specializați. În majoritatea sarcinilor NLP, unele cuvinte sunt mai relevante decât altele.\n",
"\n",
"**TF-IDF** înseamnă **frecvența termenilor - frecvența inversă a documentelor**. Este o variație a modelului bag-of-words, unde, în loc de o valoare binară 0/1 care indică apariția unui cuvânt într-un document, se folosește o valoare în virgulă mobilă, care este legată de frecvența apariției cuvântului în corpus.\n",
"\n",
"Mai formal, greutatea $w_{ij}$ a unui cuvânt $i$ în documentul $j$ este definită astfel:\n",
"$$\n",
"w_{ij} = tf_{ij}\\times\\log({N\\over df_i})\n",
"$$\n",
"unde\n",
"* $tf_{ij}$ este numărul de apariții ale lui $i$ în $j$, adică valoarea BoW pe care am văzut-o anterior\n",
"* $N$ este numărul de documente din colecție\n",
"* $df_i$ este numărul de documente care conțin cuvântul $i$ în întreaga colecție\n",
"\n",
"Valoarea TF-IDF $w_{ij}$ crește proporțional cu numărul de ori în care un cuvânt apare într-un document și este ajustată în funcție de numărul de documente din corpus care conțin cuvântul, ceea ce ajută la corectarea faptului că unele cuvinte apar mai frecvent decât altele. De exemplu, dacă cuvântul apare în *fiecare* document din colecție, $df_i=N$, și $w_{ij}=0$, iar acei termeni ar fi complet ignorați.\n",
"\n",
"Poți crea cu ușurință o vectorizare TF-IDF a textului folosind Scikit Learn:\n"
]
},
{
"cell_type": "code",
"execution_count": 16,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"array([[0.43381609, 0. , 0.43381609, 0. , 0.65985664,\n",
" 0.43381609, 0. , 0. , 0. , 0. ,\n",
" 0. , 0. , 0. , 0. , 0. ,\n",
" 0. ]])"
]
},
"execution_count": 16,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"from sklearn.feature_extraction.text import TfidfVectorizer\n",
"vectorizer = TfidfVectorizer(ngram_range=(1,2))\n",
"vectorizer.fit_transform(corpus)\n",
"vectorizer.transform(['My dog likes hot dogs on a hot day.']).toarray()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"În Keras, stratul `TextVectorization` poate calcula automat frecvențele TF-IDF prin transmiterea parametrului `output_mode='tf-idf'`. Să repetăm codul pe care l-am folosit mai sus pentru a vedea dacă utilizarea TF-IDF crește acuratețea:\n"
]
},
{
"cell_type": "code",
"execution_count": 17,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Training vectorizer\n",
"938/938 [==============================] - 12s 12ms/step - loss: 0.4197 - acc: 0.8662 - val_loss: 0.3432 - val_acc: 0.8849\n"
]
},
{
"data": {
"text/plain": [
"<keras.callbacks.History at 0x20c729dfd30>"
]
},
"execution_count": 17,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"model = keras.models.Sequential([\n",
" keras.layers.experimental.preprocessing.TextVectorization(max_tokens=vocab_size,output_mode='tf-idf'),\n",
" keras.layers.Dense(4,input_shape=(vocab_size,), activation='softmax')\n",
"])\n",
"print(\"Training vectorizer\")\n",
"model.layers[0].adapt(ds_train.take(500).map(extract_text))\n",
"model.compile(loss='sparse_categorical_crossentropy',optimizer='adam',metrics=['acc'])\n",
"model.fit(ds_train.map(tupelize).batch(batch_size),validation_data=ds_test.map(tupelize).batch(batch_size))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Concluzie\n",
"\n",
"Deși reprezentările TF-IDF atribuie greutăți de frecvență diferitelor cuvinte, ele nu pot reprezenta sensul sau ordinea. Așa cum a spus faimosul lingvist J. R. Firth în 1935: „Sensul complet al unui cuvânt este întotdeauna contextual, iar niciun studiu al sensului în afara contextului nu poate fi luat în serios.” Vom învăța mai târziu în curs cum să capturăm informațiile contextuale din text folosind modelarea limbajului.\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"\n---\n\n**Declinare de responsabilitate**: \nAcest document a fost tradus folosind serviciul de traducere AI [Co-op Translator](https://github.com/Azure/co-op-translator). Deși ne străduim să asigurăm acuratețea, vă rugăm să fiți conștienți că traducerile automate pot conține erori sau inexactități. Documentul original în limba sa natală ar trebui considerat sursa autoritară. Pentru informații critice, se recomandă traducerea profesională realizată de un specialist uman. Nu ne asumăm responsabilitatea pentru eventualele neînțelegeri sau interpretări greșite care pot apărea din utilizarea acestei traduceri.\n"
]
}
],
"metadata": {
"interpreter": {
"hash": "0cb620c6d4b9f7a635928804c26cf22403d89d98d79684e4529119355ee6d5a5"
},
"kernel_info": {
"name": "conda-env-py37_tensorflow-py"
},
"kernelspec": {
"display_name": "py37_tensorflow",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.8.12"
},
"nteract": {
"version": "nteract-front-end@1.0.0"
},
"coopTranslator": {
"original_hash": "19b43951d55b377a76209c24c1f017e4",
"translation_date": "2025-08-30T01:15:48+00:00",
"source_file": "lessons/5-NLP/13-TextRep/TextRepresentationTF.ipynb",
"language_code": "ro"
}
},
"nbformat": 4,
"nbformat_minor": 4
}

View File

@ -0,0 +1,724 @@
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Încapsulări\n",
"\n",
"În exemplul anterior, am lucrat cu vectori de tip bag-of-words de dimensiuni mari, având lungimea `vocab_size`, și am convertit explicit din vectori de reprezentare pozițională de dimensiuni mici în reprezentări sparse de tip one-hot. Această reprezentare one-hot nu este eficientă din punct de vedere al memoriei și, în plus, fiecare cuvânt este tratat independent de celelalte, adică vectorii codificați one-hot nu exprimă nicio similaritate semantică între cuvinte.\n",
"\n",
"În această unitate, vom continua să explorăm setul de date **News AG**. Pentru început, să încărcăm datele și să obținem câteva definiții din notebook-ul anterior.\n"
]
},
{
"cell_type": "code",
"execution_count": 1,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Loading dataset...\n"
]
},
{
"name": "stderr",
"output_type": "stream",
"text": [
"d:\\WORK\\ai-for-beginners\\5-NLP\\14-Embeddings\\data\\train.csv: 29.5MB [00:01, 18.8MB/s] \n",
"d:\\WORK\\ai-for-beginners\\5-NLP\\14-Embeddings\\data\\test.csv: 1.86MB [00:00, 11.2MB/s] \n"
]
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"Building vocab...\n",
"Vocab size = 95812\n"
]
}
],
"source": [
"import torch\n",
"import torchtext\n",
"import numpy as np\n",
"from torchnlp import *\n",
"train_dataset, test_dataset, classes, vocab = load_dataset()\n",
"vocab_size = len(vocab)\n",
"print(\"Vocab size = \",vocab_size)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Ce este embedding-ul?\n",
"\n",
"Ideea de **embedding** este de a reprezenta cuvintele prin vectori densi de dimensiuni mai mici, care reflectă într-un fel semnificația semantică a unui cuvânt. Vom discuta mai târziu cum să construim embedding-uri semnificative pentru cuvinte, dar deocamdată să ne gândim la embedding-uri ca la o modalitate de a reduce dimensiunea unui vector de cuvânt.\n",
"\n",
"Astfel, un strat de embedding ar lua un cuvânt ca intrare și ar produce un vector de ieșire cu dimensiunea specificată `embedding_size`. Într-un fel, este foarte similar cu stratul `Linear`, dar în loc să ia un vector codificat one-hot, va putea să primească un număr de cuvânt ca intrare.\n",
"\n",
"Folosind stratul de embedding ca prim strat în rețeaua noastră, putem trece de la modelul bag-of-words la modelul **embedding bag**, unde mai întâi convertim fiecare cuvânt din textul nostru în embedding-ul corespunzător, iar apoi calculăm o funcție de agregare peste toate aceste embedding-uri, cum ar fi `sum`, `average` sau `max`.\n",
"\n",
"![Imagine care arată un clasificator embedding pentru cinci cuvinte dintr-o secvență.](../../../../../translated_images/embedding-classifier-example.b77f021a7ee67eeec8e68bfe11636c5b97d6eaa067515a129bfb1d0034b1ac5b.ro.png)\n",
"\n",
"Rețeaua noastră neuronală de clasificare va începe cu un strat de embedding, urmat de un strat de agregare și un clasificator liniar deasupra acestuia:\n"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {},
"outputs": [],
"source": [
"class EmbedClassifier(torch.nn.Module):\n",
" def __init__(self, vocab_size, embed_dim, num_class):\n",
" super().__init__()\n",
" self.embedding = torch.nn.Embedding(vocab_size, embed_dim)\n",
" self.fc = torch.nn.Linear(embed_dim, num_class)\n",
"\n",
" def forward(self, x):\n",
" x = self.embedding(x)\n",
" x = torch.mean(x,dim=1)\n",
" return self.fc(x)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Gestionarea dimensiunii variabile a secvenței\n",
"\n",
"Ca rezultat al acestei arhitecturi, minibatch-urile pentru rețea ar trebui să fie create într-un mod specific. În unitatea anterioară, când foloseam metoda bag-of-words, toate tensori BoW dintr-un minibatch aveau aceeași dimensiune `vocab_size`, indiferent de lungimea reală a secvenței text. Odată ce trecem la încorporări de cuvinte, ajungem să avem un număr variabil de cuvinte în fiecare eșantion de text, iar atunci când combinăm aceste eșantioane în minibatch-uri, va trebui să aplicăm un padding.\n",
"\n",
"Acest lucru poate fi realizat folosind aceeași tehnică de furnizare a funcției `collate_fn` sursei de date:\n"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {},
"outputs": [],
"source": [
"def padify(b):\n",
" # b is the list of tuples of length batch_size\n",
" # - first element of a tuple = label, \n",
" # - second = feature (text sequence)\n",
" # build vectorized sequence\n",
" v = [encode(x[1]) for x in b]\n",
" # first, compute max length of a sequence in this minibatch\n",
" l = max(map(len,v))\n",
" return ( # tuple of two tensors - labels and features\n",
" torch.LongTensor([t[0]-1 for t in b]),\n",
" torch.stack([torch.nn.functional.pad(torch.tensor(t),(0,l-len(t)),mode='constant',value=0) for t in v])\n",
" )\n",
"\n",
"train_loader = torch.utils.data.DataLoader(train_dataset, batch_size=16, collate_fn=padify, shuffle=True)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Antrenarea clasificatorului de embedding\n",
"\n",
"Acum că am definit un dataloader corespunzător, putem antrena modelul folosind funcția de antrenare pe care am definit-o în unitatea precedentă:\n"
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"3200: acc=0.6415625\n",
"6400: acc=0.6865625\n",
"9600: acc=0.7103125\n",
"12800: acc=0.726953125\n",
"16000: acc=0.739375\n",
"19200: acc=0.75046875\n",
"22400: acc=0.7572321428571429\n"
]
},
{
"data": {
"text/plain": [
"(0.889799795315499, 0.7623160588611644)"
]
},
"execution_count": 4,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"net = EmbedClassifier(vocab_size,32,len(classes)).to(device)\n",
"train_epoch(net,train_loader, lr=1, epoch_size=25000)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"> **Notă**: Aici antrenăm doar pentru 25k de înregistrări (mai puțin de un întreg epoc) pentru a economisi timp, dar puteți continua antrenamentul, scrie o funcție pentru a antrena pentru mai multe epoci și experimentați cu parametrul ratei de învățare pentru a obține o acuratețe mai mare. Ar trebui să puteți ajunge la o acuratețe de aproximativ 90%.\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Stratul EmbeddingBag și Reprezentarea Secvențelor de Lungime Variabilă\n",
"\n",
"În arhitectura anterioară, a fost necesar să completăm toate secvențele la aceeași lungime pentru a le încadra într-un minibatch. Aceasta nu este cea mai eficientă metodă de a reprezenta secvențele de lungime variabilă - o altă abordare ar fi utilizarea unui vector de **offset**, care ar conține offset-urile tuturor secvențelor stocate într-un singur vector mare.\n",
"\n",
"![Imagine care arată o reprezentare a secvențelor cu offset](../../../../../translated_images/offset-sequence-representation.eb73fcefb29b46eecfbe74466077cfeb7c0f93a4f254850538a2efbc63517479.ro.png)\n",
"\n",
"> **Note**: În imaginea de mai sus, este prezentată o secvență de caractere, dar în exemplul nostru lucrăm cu secvențe de cuvinte. Totuși, principiul general de reprezentare a secvențelor cu un vector de offset rămâne același.\n",
"\n",
"Pentru a lucra cu reprezentarea prin offset, utilizăm stratul [`EmbeddingBag`](https://pytorch.org/docs/stable/generated/torch.nn.EmbeddingBag.html). Este similar cu `Embedding`, dar ia ca intrare un vector de conținut și un vector de offset și include, de asemenea, un strat de agregare, care poate fi `mean`, `sum` sau `max`.\n",
"\n",
"Iată o rețea modificată care utilizează `EmbeddingBag`:\n"
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {},
"outputs": [],
"source": [
"class EmbedClassifier(torch.nn.Module):\n",
" def __init__(self, vocab_size, embed_dim, num_class):\n",
" super().__init__()\n",
" self.embedding = torch.nn.EmbeddingBag(vocab_size, embed_dim)\n",
" self.fc = torch.nn.Linear(embed_dim, num_class)\n",
"\n",
" def forward(self, text, off):\n",
" x = self.embedding(text, off)\n",
" return self.fc(x)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Pentru a pregăti setul de date pentru antrenare, trebuie să furnizăm o funcție de conversie care va pregăti vectorul de offset:\n"
]
},
{
"cell_type": "code",
"execution_count": 6,
"metadata": {},
"outputs": [],
"source": [
"def offsetify(b):\n",
" # first, compute data tensor from all sequences\n",
" x = [torch.tensor(encode(t[1])) for t in b]\n",
" # now, compute the offsets by accumulating the tensor of sequence lengths\n",
" o = [0] + [len(t) for t in x]\n",
" o = torch.tensor(o[:-1]).cumsum(dim=0)\n",
" return ( \n",
" torch.LongTensor([t[0]-1 for t in b]), # labels\n",
" torch.cat(x), # text \n",
" o\n",
" )\n",
"\n",
"train_loader = torch.utils.data.DataLoader(train_dataset, batch_size=16, collate_fn=offsetify, shuffle=True)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Notă, că spre deosebire de toate exemplele anterioare, rețeaua noastră acceptă acum doi parametri: vectorul de date și vectorul de offset, care au dimensiuni diferite. În mod similar, loader-ul nostru de date ne oferă acum 3 valori în loc de 2: atât vectorii de text, cât și cei de offset sunt furnizați ca caracteristici. Prin urmare, trebuie să ajustăm ușor funcția noastră de antrenament pentru a ține cont de acest lucru:\n"
]
},
{
"cell_type": "code",
"execution_count": 7,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"3200: acc=0.6153125\n",
"6400: acc=0.6615625\n",
"9600: acc=0.6932291666666667\n",
"12800: acc=0.715078125\n",
"16000: acc=0.7270625\n",
"19200: acc=0.7382291666666667\n",
"22400: acc=0.7486160714285715\n"
]
},
{
"data": {
"text/plain": [
"(22.771553103007037, 0.7551983365323096)"
]
},
"execution_count": 7,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"net = EmbedClassifier(vocab_size,32,len(classes)).to(device)\n",
"\n",
"def train_epoch_emb(net,dataloader,lr=0.01,optimizer=None,loss_fn = torch.nn.CrossEntropyLoss(),epoch_size=None, report_freq=200):\n",
" optimizer = optimizer or torch.optim.Adam(net.parameters(),lr=lr)\n",
" loss_fn = loss_fn.to(device)\n",
" net.train()\n",
" total_loss,acc,count,i = 0,0,0,0\n",
" for labels,text,off in dataloader:\n",
" optimizer.zero_grad()\n",
" labels,text,off = labels.to(device), text.to(device), off.to(device)\n",
" out = net(text, off)\n",
" loss = loss_fn(out,labels) #cross_entropy(out,labels)\n",
" loss.backward()\n",
" optimizer.step()\n",
" total_loss+=loss\n",
" _,predicted = torch.max(out,1)\n",
" acc+=(predicted==labels).sum()\n",
" count+=len(labels)\n",
" i+=1\n",
" if i%report_freq==0:\n",
" print(f\"{count}: acc={acc.item()/count}\")\n",
" if epoch_size and count>epoch_size:\n",
" break\n",
" return total_loss.item()/count, acc.item()/count\n",
"\n",
"\n",
"train_epoch_emb(net,train_loader, lr=4, epoch_size=25000)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Încapsulări Semantice: Word2Vec\n",
"\n",
"În exemplul nostru anterior, stratul de învățare al modelului a fost antrenat să mapeze cuvintele la o reprezentare vectorială, însă această reprezentare nu avea prea multă semnificație semantică. Ar fi util să învățăm o astfel de reprezentare vectorială, în care cuvintele similare sau sinonime să corespundă unor vectori care sunt apropiați unul de celălalt în funcție de o anumită distanță vectorială (de exemplu, distanța euclidiană).\n",
"\n",
"Pentru a realiza acest lucru, trebuie să pre-antrenăm modelul de învățare pe o colecție mare de texte într-un mod specific. Una dintre primele metode de antrenare a încapsulărilor semantice se numește [Word2Vec](https://en.wikipedia.org/wiki/Word2vec). Aceasta se bazează pe două arhitecturi principale utilizate pentru a produce o reprezentare distribuită a cuvintelor:\n",
"\n",
" - **Continuous bag-of-words** (CBoW) — în această arhitectură, modelul este antrenat să prezică un cuvânt pe baza contextului din jur. Având ngrama $(W_{-2},W_{-1},W_0,W_1,W_2)$, scopul modelului este să prezică $W_0$ pe baza $(W_{-2},W_{-1},W_1,W_2)$.\n",
" - **Continuous skip-gram** este opusul CBoW. Modelul folosește fereastra de cuvinte contextuale din jur pentru a prezice cuvântul curent.\n",
"\n",
"CBoW este mai rapid, în timp ce skip-gram este mai lent, dar oferă o reprezentare mai bună pentru cuvintele rare.\n",
"\n",
"![Imagine care arată algoritmii CBoW și Skip-Gram pentru conversia cuvintelor în vectori.](../../../../../translated_images/example-algorithms-for-converting-words-to-vectors.fbe9207a726922f6f0f5de66427e8a6eda63809356114e28fb1fa5f4a83ebda7.ro.png)\n",
"\n",
"Pentru a experimenta cu încapsulările word2vec pre-antrenate pe setul de date Google News, putem folosi biblioteca **gensim**. Mai jos găsim cuvintele cele mai similare cu 'neural'.\n",
"\n",
"> **Note:** Când creați pentru prima dată vectori de cuvinte, descărcarea acestora poate dura ceva timp!\n"
]
},
{
"cell_type": "code",
"execution_count": 8,
"metadata": {},
"outputs": [],
"source": [
"import gensim.downloader as api\n",
"w2v = api.load('word2vec-google-news-300')"
]
},
{
"cell_type": "code",
"execution_count": 9,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"neuronal -> 0.7804799675941467\n",
"neurons -> 0.7326500415802002\n",
"neural_circuits -> 0.7252851724624634\n",
"neuron -> 0.7174385190010071\n",
"cortical -> 0.6941086649894714\n",
"brain_circuitry -> 0.6923246383666992\n",
"synaptic -> 0.6699118614196777\n",
"neural_circuitry -> 0.6638563275337219\n",
"neurochemical -> 0.6555314064025879\n",
"neuronal_activity -> 0.6531826257705688\n"
]
}
],
"source": [
"for w,p in w2v.most_similar('neural'):\n",
" print(f\"{w} -> {p}\")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Putem, de asemenea, să calculăm încorporările vectoriale din cuvânt, pentru a fi utilizate în antrenarea modelului de clasificare (afișăm doar primele 20 de componente ale vectorului pentru claritate):\n"
]
},
{
"cell_type": "code",
"execution_count": 10,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"array([ 0.01226807, 0.06225586, 0.10693359, 0.05810547, 0.23828125,\n",
" 0.03686523, 0.05151367, -0.20703125, 0.01989746, 0.10058594,\n",
" -0.03759766, -0.1015625 , -0.15820312, -0.08105469, -0.0390625 ,\n",
" -0.05053711, 0.16015625, 0.2578125 , 0.10058594, -0.25976562],\n",
" dtype=float32)"
]
},
"execution_count": 10,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"w2v.word_vec('play')[:20]"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Lucrul grozav despre încorporările semantice este că poți manipula codificarea vectorială pentru a schimba semantica. De exemplu, putem cere să găsim un cuvânt, a cărui reprezentare vectorială să fie cât mai apropiată de cuvintele *rege* și *femeie*, și cât mai îndepărtată de cuvântul *bărbat*:\n"
]
},
{
"cell_type": "code",
"execution_count": 10,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"('queen', 0.7118192911148071)"
]
},
"execution_count": 10,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"w2v.most_similar(positive=['king','woman'],negative=['man'])[0]"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Atât CBoW, cât și Skip-Grams sunt încorporări „predictive”, în sensul că iau în considerare doar contexte locale. Word2Vec nu profită de contextul global.\n",
"\n",
"**FastText** se bazează pe Word2Vec prin învățarea reprezentărilor vectoriale pentru fiecare cuvânt și pentru n-gramele de caractere găsite în interiorul fiecărui cuvânt. Valorile reprezentărilor sunt apoi mediate într-un singur vector la fiecare pas de antrenament. Deși acest lucru adaugă multă calculare suplimentară în etapa de pre-antrenare, permite încorporărilor de cuvinte să encodeze informații la nivel de sub-cuvânt.\n",
"\n",
"O altă metodă, **GloVe**, valorifică ideea matricei de co-apariție, utilizând metode neuronale pentru a descompune matricea de co-apariție în vectori de cuvinte mai expresivi și non-liniari.\n",
"\n",
"Poți experimenta cu exemplul schimbând încorporările în FastText și GloVe, deoarece gensim suportă mai multe modele diferite de încorporare a cuvintelor.\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Utilizarea Embedding-urilor Pre-Antrenate în PyTorch\n",
"\n",
"Putem modifica exemplul de mai sus pentru a pre-popula matricea din stratul nostru de embedding cu embedding-uri semantice, cum ar fi Word2Vec. Trebuie să ținem cont de faptul că vocabularele embedding-urilor pre-antrenate și ale corpusului nostru de text probabil nu se vor potrivi, așa că vom inițializa greutățile pentru cuvintele lipsă cu valori random:\n"
]
},
{
"cell_type": "code",
"execution_count": 11,
"metadata": {
"tags": []
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Embedding size: 300\n",
"Populating matrix, this will take some time...Done, found 41080 words, 54732 words missing\n"
]
}
],
"source": [
"embed_size = len(w2v.get_vector('hello'))\n",
"print(f'Embedding size: {embed_size}')\n",
"\n",
"net = EmbedClassifier(vocab_size,embed_size,len(classes))\n",
"\n",
"print('Populating matrix, this will take some time...',end='')\n",
"found, not_found = 0,0\n",
"for i,w in enumerate(vocab.get_itos()):\n",
" try:\n",
" net.embedding.weight[i].data = torch.tensor(w2v.get_vector(w))\n",
" found+=1\n",
" except:\n",
" net.embedding.weight[i].data = torch.normal(0.0,1.0,(embed_size,))\n",
" not_found+=1\n",
"\n",
"print(f\"Done, found {found} words, {not_found} words missing\")\n",
"net = net.to(device)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Acum să antrenăm modelul nostru. Rețineți că timpul necesar pentru a antrena modelul este semnificativ mai mare decât în exemplul anterior, datorită dimensiunii mai mari a stratului de încorporare și, astfel, unui număr mult mai mare de parametri. De asemenea, din acest motiv, s-ar putea să fie nevoie să antrenăm modelul nostru pe mai multe exemple dacă dorim să evităm supraînvățarea.\n"
]
},
{
"cell_type": "code",
"execution_count": 12,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"3200: acc=0.6359375\n",
"6400: acc=0.68109375\n",
"9600: acc=0.7067708333333333\n",
"12800: acc=0.723671875\n",
"16000: acc=0.73625\n",
"19200: acc=0.7463541666666667\n",
"22400: acc=0.7560714285714286\n"
]
},
{
"data": {
"text/plain": [
"(214.1013875559821, 0.7626759436980166)"
]
},
"execution_count": 12,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"train_epoch_emb(net,train_loader, lr=4, epoch_size=25000)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"În cazul nostru, nu observăm o creștere semnificativă a acurateței, cel mai probabil din cauza vocabularului destul de diferit. \n",
"Pentru a depăși problema vocabularului diferit, putem folosi una dintre următoarele soluții: \n",
"* Re-antrenarea modelului word2vec pe vocabularul nostru \n",
"* Încărcarea setului nostru de date folosind vocabularul din modelul word2vec pre-antrenat. Vocabularul utilizat pentru încărcarea setului de date poate fi specificat în timpul încărcării. \n",
"\n",
"A doua abordare pare mai ușoară, mai ales deoarece cadrul `torchtext` din PyTorch conține suport integrat pentru embeddings. Putem, de exemplu, să instanțiem un vocabular bazat pe GloVe în următorul mod: \n"
]
},
{
"cell_type": "code",
"execution_count": 14,
"metadata": {},
"outputs": [
{
"name": "stderr",
"output_type": "stream",
"text": [
"100%|█████████▉| 399999/400000 [00:15<00:00, 25411.14it/s]\n"
]
}
],
"source": [
"vocab = torchtext.vocab.GloVe(name='6B', dim=50)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Vocabularul încărcat are următoarele operațiuni de bază: \n",
"* Dicționarul `vocab.stoi` ne permite să convertim un cuvânt în indexul său din dicționar \n",
"* `vocab.itos` face opusul - convertește un număr în cuvânt \n",
"* `vocab.vectors` este matricea de vectori de încorporare, așa că pentru a obține încorporarea unui cuvânt `s`, trebuie să folosim `vocab.vectors[vocab.stoi[s]]` \n",
"\n",
"Iată un exemplu de manipulare a încorporărilor pentru a demonstra ecuația **kind-man+woman = queen** (a trebuit să ajustez puțin coeficientul pentru a funcționa): \n"
]
},
{
"cell_type": "code",
"execution_count": 15,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"'queen'"
]
},
"execution_count": 15,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"# get the vector corresponding to kind-man+woman\n",
"qvec = vocab.vectors[vocab.stoi['king']]-vocab.vectors[vocab.stoi['man']]+1.3*vocab.vectors[vocab.stoi['woman']]\n",
"# find the index of the closest embedding vector \n",
"d = torch.sum((vocab.vectors-qvec)**2,dim=1)\n",
"min_idx = torch.argmin(d)\n",
"# find the corresponding word\n",
"vocab.itos[min_idx]"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Pentru a antrena clasificatorul folosind acele embeddings, mai întâi trebuie să codificăm setul nostru de date folosind vocabularul GloVe:\n"
]
},
{
"cell_type": "code",
"execution_count": 16,
"metadata": {},
"outputs": [],
"source": [
"def offsetify(b):\n",
" # first, compute data tensor from all sequences\n",
" x = [torch.tensor(encode(t[1],voc=vocab)) for t in b] # pass the instance of vocab to encode function!\n",
" # now, compute the offsets by accumulating the tensor of sequence lengths\n",
" o = [0] + [len(t) for t in x]\n",
" o = torch.tensor(o[:-1]).cumsum(dim=0)\n",
" return ( \n",
" torch.LongTensor([t[0]-1 for t in b]), # labels\n",
" torch.cat(x), # text \n",
" o\n",
" )"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Așa cum am văzut mai sus, toate încorporările vectoriale sunt stocate în matricea `vocab.vectors`. Acest lucru face extrem de ușoară încărcarea acelor greutăți în greutățile stratului de încorporare folosind copierea simplă:\n"
]
},
{
"cell_type": "code",
"execution_count": 17,
"metadata": {},
"outputs": [],
"source": [
"net = EmbedClassifier(len(vocab),len(vocab.vectors[0]),len(classes))\n",
"net.embedding.weight.data = vocab.vectors\n",
"net = net.to(device)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": []
},
{
"cell_type": "code",
"execution_count": 18,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"3200: acc=0.6271875\n",
"6400: acc=0.68078125\n",
"9600: acc=0.7030208333333333\n",
"12800: acc=0.71984375\n",
"16000: acc=0.7346875\n",
"19200: acc=0.7455729166666667\n",
"22400: acc=0.7529464285714286\n"
]
},
{
"data": {
"text/plain": [
"(35.53972978646833, 0.7575175943698017)"
]
},
"execution_count": 18,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"train_loader = torch.utils.data.DataLoader(train_dataset, batch_size=16, collate_fn=offsetify, shuffle=True)\n",
"train_epoch_emb(net,train_loader, lr=4, epoch_size=25000)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Unul dintre motivele pentru care nu observăm o creștere semnificativă a acurateței este faptul că unele cuvinte din setul nostru de date lipsesc din vocabularul GloVe pre-antrenat și, prin urmare, sunt practic ignorate. Pentru a depăși acest fapt, putem antrena propriile noastre embedding-uri pe setul nostru de date.\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Încadrări Contextuale\n",
"\n",
"O limitare importantă a reprezentărilor tradiționale de încadrare pre-antrenate, cum ar fi Word2Vec, este problema dezambiguizării sensului cuvintelor. Deși încadrările pre-antrenate pot surprinde o parte din sensul cuvintelor în context, fiecare posibil sens al unui cuvânt este codificat în aceeași încadrare. Acest lucru poate cauza probleme în modelele ulterioare, deoarece multe cuvinte, cum ar fi cuvântul „play”, au sensuri diferite în funcție de contextul în care sunt utilizate.\n",
"\n",
"De exemplu, cuvântul „play” în aceste două propoziții are sensuri destul de diferite:\n",
"- Am fost la o **piesă** de teatru.\n",
"- John vrea să se **joace** cu prietenii săi.\n",
"\n",
"Încadrările pre-antrenate de mai sus reprezintă ambele sensuri ale cuvântului „play” în aceeași încadrare. Pentru a depăși această limitare, trebuie să construim încadrări bazate pe **modelul lingvistic**, care este antrenat pe un corpus mare de text și *știe* cum pot fi utilizate cuvintele în contexte diferite. Discutarea încadrărilor contextuale depășește scopul acestui tutorial, dar vom reveni la ele atunci când vom vorbi despre modelele lingvistice în unitatea următoare.\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"\n---\n\n**Declinare de responsabilitate**: \nAcest document a fost tradus folosind serviciul de traducere AI [Co-op Translator](https://github.com/Azure/co-op-translator). Deși ne străduim să asigurăm acuratețea, vă rugăm să rețineți că traducerile automate pot conține erori sau inexactități. Documentul original în limba sa natală ar trebui considerat sursa autoritară. Pentru informații critice, se recomandă traducerea profesională realizată de un specialist uman. Nu ne asumăm responsabilitatea pentru eventualele neînțelegeri sau interpretări greșite care pot apărea din utilizarea acestei traduceri.\n"
]
}
],
"metadata": {
"interpreter": {
"hash": "0cb620c6d4b9f7a635928804c26cf22403d89d98d79684e4529119355ee6d5a5"
},
"kernelspec": {
"display_name": "py37_pytorch",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.8.12"
},
"coopTranslator": {
"original_hash": "f50b026abce5cf36783a560ea72cb9b1",
"translation_date": "2025-08-30T01:08:01+00:00",
"source_file": "lessons/5-NLP/14-Embeddings/EmbeddingsPyTorch.ipynb",
"language_code": "ro"
}
},
"nbformat": 4,
"nbformat_minor": 2
}

View File

@ -0,0 +1,695 @@
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Încapsulări\n",
"\n",
"În exemplul anterior, am lucrat cu vectori de tip bag-of-words de dimensiuni mari, având lungimea `vocab_size`, și am convertit explicit vectorii de reprezentare pozițională de dimensiuni mici în reprezentări sparse de tip one-hot. Această reprezentare one-hot nu este eficientă din punct de vedere al memoriei. În plus, fiecare cuvânt este tratat independent de celelalte, astfel încât vectorii codificați one-hot nu exprimă similitudini semantice între cuvinte.\n",
"\n",
"În această unitate, vom continua să explorăm setul de date **News AG**. Pentru început, să încărcăm datele și să obținem câteva definiții din unitatea anterioară.\n"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {},
"outputs": [],
"source": [
"import tensorflow as tf\n",
"from tensorflow import keras\n",
"import tensorflow_datasets as tfds\n",
"import numpy as np\n",
"\n",
"ds_train, ds_test = tfds.load('ag_news_subset').values()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Ce este un embedding?\n",
"\n",
"Ideea de **embedding** este de a reprezenta cuvintele folosind vectori densi de dimensiuni mai mici, care reflectă semnificația semantică a cuvântului. Vom discuta mai târziu cum să construim embedding-uri semnificative pentru cuvinte, dar deocamdată să ne gândim la embedding-uri ca la o modalitate de a reduce dimensiunea unui vector de cuvinte.\n",
"\n",
"Astfel, un strat de embedding primește un cuvânt ca intrare și produce un vector de ieșire cu dimensiunea specificată `embedding_size`. Într-un fel, este foarte similar cu un strat `Dense`, dar în loc să primească un vector codificat one-hot ca intrare, poate primi un număr corespunzător cuvântului.\n",
"\n",
"Folosind un strat de embedding ca prim strat în rețeaua noastră, putem trece de la modelul bag-of-words la un model **embedding bag**, unde mai întâi convertim fiecare cuvânt din textul nostru în embedding-ul corespunzător, iar apoi calculăm o funcție agregată pentru toate aceste embedding-uri, cum ar fi `sum`, `average` sau `max`.\n",
"\n",
"![Imagine care arată un clasificator embedding pentru cinci cuvinte dintr-o secvență.](../../../../../translated_images/embedding-classifier-example.b77f021a7ee67eeec8e68bfe11636c5b97d6eaa067515a129bfb1d0034b1ac5b.ro.png)\n",
"\n",
"Rețeaua noastră neurală de clasificare constă din următoarele straturi:\n",
"\n",
"* Strat `TextVectorization`, care primește un șir de caractere ca intrare și produce un tensor de numere de tokeni. Vom specifica o dimensiune rezonabilă pentru vocabular, `vocab_size`, și vom ignora cuvintele utilizate mai rar. Forma de intrare va fi 1, iar forma de ieșire va fi $n$, deoarece vom obține $n$ tokeni ca rezultat, fiecare dintre ei conținând numere de la 0 la `vocab_size`.\n",
"* Strat `Embedding`, care primește $n$ numere și reduce fiecare număr la un vector dens de o lungime dată (100 în exemplul nostru). Astfel, tensorul de intrare cu forma $n$ va fi transformat într-un tensor $n\\times 100$.\n",
"* Strat de agregare, care calculează media acestui tensor de-a lungul primei axe, adică va calcula media tuturor celor $n$ tensori de intrare corespunzători diferitelor cuvinte. Pentru a implementa acest strat, vom folosi un strat `Lambda` și vom transmite funcția pentru a calcula media. Ieșirea va avea forma 100 și va fi reprezentarea numerică a întregii secvențe de intrare.\n",
"* Clasificator final `Dense` liniar.\n"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Model: \"sequential\"\n",
"_________________________________________________________________\n",
" Layer (type) Output Shape Param # \n",
"=================================================================\n",
" text_vectorization (TextVec (None, None) 0 \n",
" torization) \n",
" \n",
" embedding (Embedding) (None, None, 100) 3000000 \n",
" \n",
" lambda (Lambda) (None, 100) 0 \n",
" \n",
" dense (Dense) (None, 4) 404 \n",
" \n",
"=================================================================\n",
"Total params: 3,000,404\n",
"Trainable params: 3,000,404\n",
"Non-trainable params: 0\n",
"_________________________________________________________________\n"
]
}
],
"source": [
"vocab_size = 30000\n",
"batch_size = 128\n",
"\n",
"vectorizer = keras.layers.experimental.preprocessing.TextVectorization(max_tokens=vocab_size,input_shape=(1,))\n",
"\n",
"model = keras.models.Sequential([\n",
" vectorizer, \n",
" keras.layers.Embedding(vocab_size,100),\n",
" keras.layers.Lambda(lambda x: tf.reduce_mean(x,axis=1)),\n",
" keras.layers.Dense(4, activation='softmax')\n",
"])\n",
"model.summary()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"În rezumatul `summary`, în coloana **output shape**, prima dimensiune a tensorului `None` corespunde dimensiunii minibatch-ului, iar a doua corespunde lungimii secvenței de tokeni. Toate secvențele de tokeni din minibatch au lungimi diferite. Vom discuta cum să gestionăm acest aspect în secțiunea următoare.\n",
"\n",
"Acum să antrenăm rețeaua:\n"
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Training vectorizer\n",
"938/938 [==============================] - 20s 20ms/step - loss: 0.7891 - acc: 0.8155 - val_loss: 0.4470 - val_acc: 0.8642\n"
]
},
{
"data": {
"text/plain": [
"<keras.callbacks.History at 0x22255515100>"
]
},
"execution_count": 4,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"def extract_text(x):\n",
" return x['title']+' '+x['description']\n",
"\n",
"def tupelize(x):\n",
" return (extract_text(x),x['label'])\n",
"\n",
"print(\"Training vectorizer\")\n",
"vectorizer.adapt(ds_train.take(500).map(extract_text))\n",
"\n",
"model.compile(loss='sparse_categorical_crossentropy',metrics=['acc'])\n",
"model.fit(ds_train.map(tupelize).batch(batch_size),validation_data=ds_test.map(tupelize).batch(batch_size))"
]
},
{
"cell_type": "markdown",
"metadata": {
"nteract": {
"transient": {
"deleting": false
}
}
},
"source": [
"> **Notă** că construim vectorizatorul pe baza unui subset de date. Acest lucru se face pentru a accelera procesul și poate duce la o situație în care nu toți tokenii din textul nostru sunt prezenți în vocabular. În acest caz, acei tokeni vor fi ignorați, ceea ce poate duce la o acuratețe ușor mai scăzută. Totuși, în viața reală, un subset de text oferă adesea o estimare bună a vocabularului.\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Gestionarea dimensiunilor variabile ale secvențelor\n",
"\n",
"Să înțelegem cum are loc antrenarea în minibatch-uri. În exemplul de mai sus, tensorul de intrare are dimensiunea 1, iar noi folosim minibatch-uri de lungime 128, astfel încât dimensiunea reală a tensorului este $128 \\times 1$. Totuși, numărul de tokeni din fiecare propoziție este diferit. Dacă aplicăm stratul `TextVectorization` unui singur input, numărul de tokeni returnați este diferit, în funcție de modul în care textul este tokenizat:\n"
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"tf.Tensor([ 1 45], shape=(2,), dtype=int64)\n",
"tf.Tensor([ 112 1271 1 3 1747 158], shape=(6,), dtype=int64)\n"
]
}
],
"source": [
"print(vectorizer('Hello, world!'))\n",
"print(vectorizer('I am glad to meet you!'))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Cu toate acestea, atunci când aplicăm vectorizatorul la mai multe secvențe, acesta trebuie să producă un tensor de formă dreptunghiulară, astfel încât să umple elementele neutilizate cu tokenul PAD (care în cazul nostru este zero):\n"
]
},
{
"cell_type": "code",
"execution_count": 6,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"<tf.Tensor: shape=(2, 6), dtype=int64, numpy=\n",
"array([[ 1, 45, 0, 0, 0, 0],\n",
" [ 112, 1271, 1, 3, 1747, 158]], dtype=int64)>"
]
},
"execution_count": 6,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"vectorizer(['Hello, world!','I am glad to meet you!'])"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Aici putem vedea încorporările:\n"
]
},
{
"cell_type": "code",
"execution_count": 7,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"array([[[ 1.53059261e-02, 6.80514947e-02, 3.14026810e-02, ...,\n",
" -8.92002955e-02, 1.52911525e-04, -5.65562584e-02],\n",
" [ 2.57456154e-01, 2.79364467e-01, -2.03605562e-01, ...,\n",
" -2.07474351e-01, 8.31158683e-02, -2.03911960e-01],\n",
" [ 3.98201384e-02, -8.03454965e-03, 2.39790026e-02, ...,\n",
" -7.18549127e-04, 2.66963355e-02, -4.30646613e-02],\n",
" [ 3.98201384e-02, -8.03454965e-03, 2.39790026e-02, ...,\n",
" -7.18549127e-04, 2.66963355e-02, -4.30646613e-02],\n",
" [ 3.98201384e-02, -8.03454965e-03, 2.39790026e-02, ...,\n",
" -7.18549127e-04, 2.66963355e-02, -4.30646613e-02],\n",
" [ 3.98201384e-02, -8.03454965e-03, 2.39790026e-02, ...,\n",
" -7.18549127e-04, 2.66963355e-02, -4.30646613e-02]],\n",
"\n",
" [[ 1.89674050e-01, 2.61548996e-01, -3.67433839e-02, ...,\n",
" -2.07366899e-01, -1.05442435e-01, -2.36952081e-01],\n",
" [ 6.16133213e-02, 1.80511594e-01, 9.77298319e-02, ...,\n",
" -5.46628237e-02, -1.07340455e-01, -1.06589928e-01],\n",
" [ 1.53059261e-02, 6.80514947e-02, 3.14026810e-02, ...,\n",
" -8.92002955e-02, 1.52911525e-04, -5.65562584e-02],\n",
" [-4.84890305e-02, -8.41715634e-02, 1.51529670e-01, ...,\n",
" 1.28192469e-01, -7.77286515e-02, 1.26041949e-01],\n",
" [-4.17212099e-02, -5.60694858e-02, 4.08860669e-02, ...,\n",
" 8.70475471e-02, 8.92383084e-02, 1.67974353e-01],\n",
" [ 2.85779923e-01, 4.57767487e-01, 4.52292450e-02, ...,\n",
" -1.97419018e-01, -2.04659685e-01, -2.79758364e-01]]],\n",
" dtype=float32)"
]
},
"execution_count": 7,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"model.layers[1](vectorizer(['Hello, world!','I am glad to meet you!'])).numpy()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"> **Notă**: Pentru a minimiza cantitatea de umplere, în unele cazuri are sens să se sorteze toate secvențele din setul de date în ordinea lungimii crescătoare (sau, mai precis, numărul de tokeni). Acest lucru va asigura că fiecare minibatch conține secvențe de lungime similară.\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Încapsulări semantice: Word2Vec\n",
"\n",
"În exemplul anterior, stratul de încapsulare a învățat să transforme cuvintele în reprezentări vectoriale, însă aceste reprezentări nu aveau o semnificație semantică. Ar fi ideal să învățăm o reprezentare vectorială astfel încât cuvintele similare sau sinonime să corespundă unor vectori apropiați între ei în funcție de o anumită distanță vectorială (de exemplu, distanța euclidiană).\n",
"\n",
"Pentru a realiza acest lucru, trebuie să preantrenăm modelul de încapsulare pe o colecție mare de texte utilizând o tehnică precum [Word2Vec](https://en.wikipedia.org/wiki/Word2vec). Aceasta se bazează pe două arhitecturi principale folosite pentru a produce o reprezentare distribuită a cuvintelor:\n",
"\n",
" - **Continuous bag-of-words** (CBoW), unde antrenăm modelul să prezică un cuvânt pe baza contextului din jur. Având ngram-ul $(W_{-2},W_{-1},W_0,W_1,W_2)$, scopul modelului este să prezică $W_0$ pe baza $(W_{-2},W_{-1},W_1,W_2)$.\n",
" - **Continuous skip-gram** este opusul lui CBoW. Modelul folosește fereastra de cuvinte contextuale din jur pentru a prezice cuvântul curent.\n",
"\n",
"CBoW este mai rapid, iar skip-gram, deși mai lent, face o treabă mai bună în reprezentarea cuvintelor rare.\n",
"\n",
"![Imagine care ilustrează algoritmii CBoW și Skip-Gram pentru conversia cuvintelor în vectori.](../../../../../translated_images/example-algorithms-for-converting-words-to-vectors.fbe9207a726922f6f0f5de66427e8a6eda63809356114e28fb1fa5f4a83ebda7.ro.png)\n",
"\n",
"Pentru a experimenta cu încapsularea Word2Vec preantrenată pe setul de date Google News, putem folosi biblioteca **gensim**. Mai jos găsim cuvintele cele mai similare cu 'neural'.\n",
"\n",
"> **Notă:** Când creați pentru prima dată vectori de cuvinte, descărcarea acestora poate dura ceva timp!\n"
]
},
{
"cell_type": "code",
"execution_count": 8,
"metadata": {},
"outputs": [],
"source": [
"import gensim.downloader as api\n",
"w2v = api.load('word2vec-google-news-300')"
]
},
{
"cell_type": "code",
"execution_count": 12,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"neuronal -> 0.7804799675941467\n",
"neurons -> 0.7326500415802002\n",
"neural_circuits -> 0.7252851724624634\n",
"neuron -> 0.7174385190010071\n",
"cortical -> 0.6941086649894714\n",
"brain_circuitry -> 0.6923246383666992\n",
"synaptic -> 0.6699118614196777\n",
"neural_circuitry -> 0.6638563275337219\n",
"neurochemical -> 0.6555314064025879\n",
"neuronal_activity -> 0.6531826257705688\n"
]
}
],
"source": [
"for w,p in w2v.most_similar('neural'):\n",
" print(f\"{w} -> {p}\")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Putem, de asemenea, să extragem încorporarea vectorială din cuvânt, pentru a fi utilizată în antrenarea modelului de clasificare. Încorporarea are 300 de componente, dar aici arătăm doar primele 20 de componente ale vectorului pentru claritate:\n"
]
},
{
"cell_type": "code",
"execution_count": 13,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"array([ 0.01226807, 0.06225586, 0.10693359, 0.05810547, 0.23828125,\n",
" 0.03686523, 0.05151367, -0.20703125, 0.01989746, 0.10058594,\n",
" -0.03759766, -0.1015625 , -0.15820312, -0.08105469, -0.0390625 ,\n",
" -0.05053711, 0.16015625, 0.2578125 , 0.10058594, -0.25976562],\n",
" dtype=float32)"
]
},
"execution_count": 13,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"w2v['play'][:20]"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Lucrul grozav despre încorporările semantice este că poți manipula codificarea vectorială pe baza semanticii. De exemplu, putem cere să găsim un cuvânt a cărui reprezentare vectorială este cât mai aproape posibil de cuvintele *rege* și *femeie*, și cât mai departe posibil de cuvântul *bărbat*:\n"
]
},
{
"cell_type": "code",
"execution_count": 14,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"('queen', 0.7118192911148071)"
]
},
"execution_count": 14,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"w2v.most_similar(positive=['king','woman'],negative=['man'])[0]"
]
},
{
"cell_type": "markdown",
"metadata": {
"tags": []
},
"source": [
"Un exemplu de mai sus folosește ceva magie internă GenSym, dar logica de bază este de fapt destul de simplă. Un lucru interesant despre încorporări este că poți efectua operații normale pe vectori de încorporare, iar acestea ar reflecta operații asupra **semnificațiilor** cuvintelor. Exemplul de mai sus poate fi exprimat în termeni de operații pe vectori: calculăm vectorul corespunzător **REGE-BĂRBAT+FEMEIE** (operațiile `+` și `-` sunt efectuate pe reprezentările vectoriale ale cuvintelor corespunzătoare), și apoi găsim cel mai apropiat cuvânt din dicționar de acel vector:\n"
]
},
{
"cell_type": "code",
"execution_count": 15,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"'queen'"
]
},
"execution_count": 15,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"# get the vector corresponding to kind-man+woman\n",
"qvec = w2v['king']-1.7*w2v['man']+1.7*w2v['woman']\n",
"# find the index of the closest embedding vector \n",
"d = np.sum((w2v.vectors-qvec)**2,axis=1)\n",
"min_idx = np.argmin(d)\n",
"# find the corresponding word\n",
"w2v.index_to_key[min_idx]"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"> **NOTĂ**: A trebuit să adăugăm niște coeficienți mici vectorilor *man* și *woman* - încearcă să îi elimini pentru a vedea ce se întâmplă.\n",
"\n",
"Pentru a găsi vectorul cel mai apropiat, folosim mecanismele TensorFlow pentru a calcula un vector de distanțe între vectorul nostru și toți vectorii din vocabular, iar apoi găsim indexul cuvântului minim folosind `argmin`.\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Deși Word2Vec pare o metodă excelentă pentru a exprima semantica cuvintelor, are multe dezavantaje, inclusiv următoarele:\n",
"\n",
"* Atât modelele CBoW, cât și skip-gram sunt **embedding-uri predictive**, și iau în considerare doar contextul local. Word2Vec nu profită de contextul global.\n",
"* Word2Vec nu ia în considerare **morfologia** cuvintelor, adică faptul că sensul unui cuvânt poate depinde de diferite părți ale cuvântului, cum ar fi rădăcina.\n",
"\n",
"**FastText** încearcă să depășească a doua limitare și se bazează pe Word2Vec prin învățarea reprezentărilor vectoriale pentru fiecare cuvânt și pentru n-gramele de caractere găsite în interiorul fiecărui cuvânt. Valorile reprezentărilor sunt apoi mediate într-un singur vector la fiecare pas de antrenament. Deși acest lucru adaugă multă calculare suplimentară în etapa de pre-antrenare, permite embedding-urilor să encodeze informații despre sub-cuvinte.\n",
"\n",
"O altă metodă, **GloVe**, folosește o abordare diferită pentru embedding-urile de cuvinte, bazată pe factorizarea matricei cuvânt-context. Mai întâi, construiește o matrice mare care numără de câte ori apar cuvintele în diferite contexte, iar apoi încearcă să reprezinte această matrice în dimensiuni mai mici într-un mod care minimizează pierderea de reconstrucție.\n",
"\n",
"Biblioteca gensim suportă aceste embedding-uri de cuvinte, și poți experimenta cu ele schimbând codul de încărcare a modelului de mai sus.\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Utilizarea embedding-urilor pre-antrenate în Keras\n",
"\n",
"Putem modifica exemplul de mai sus pentru a prepopula matricea din stratul nostru de embedding cu embedding-uri semantice, cum ar fi Word2Vec. Vocabularul embedding-urilor pre-antrenate și cel al corpusului de text probabil nu se vor potrivi, așa că trebuie să alegem unul. Aici explorăm cele două opțiuni posibile: utilizarea vocabularului tokenizer-ului și utilizarea vocabularului embedding-urilor Word2Vec.\n",
"\n",
"### Utilizarea vocabularului tokenizer-ului\n",
"\n",
"Când folosim vocabularul tokenizer-ului, unele dintre cuvintele din vocabular vor avea embedding-uri Word2Vec corespunzătoare, iar altele vor lipsi. Având în vedere că dimensiunea vocabularului nostru este `vocab_size`, iar lungimea vectorului embedding Word2Vec este `embed_size`, stratul de embedding va fi reprezentat de o matrice de greutăți cu forma `vocab_size`$\\times$`embed_size`. Vom popula această matrice parcurgând vocabularul:\n"
]
},
{
"cell_type": "code",
"execution_count": 9,
"metadata": {
"tags": []
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Embedding size: 300\n",
"Populating matrix, this will take some time...Done, found 4551 words, 784 words missing\n"
]
}
],
"source": [
"embed_size = len(w2v.get_vector('hello'))\n",
"print(f'Embedding size: {embed_size}')\n",
"\n",
"vocab = vectorizer.get_vocabulary()\n",
"W = np.zeros((vocab_size,embed_size))\n",
"print('Populating matrix, this will take some time...',end='')\n",
"found, not_found = 0,0\n",
"for i,w in enumerate(vocab):\n",
" try:\n",
" W[i] = w2v.get_vector(w)\n",
" found+=1\n",
" except:\n",
" # W[i] = np.random.normal(0.0,0.3,size=(embed_size,))\n",
" not_found+=1\n",
"\n",
"print(f\"Done, found {found} words, {not_found} words missing\")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Pentru cuvintele care nu sunt prezente în vocabularul Word2Vec, putem fie să le lăsăm ca zerouri, fie să generăm un vector aleatoriu.\n",
"\n",
"Acum putem defini un strat de embedding cu greutăți pre-antrenate:\n"
]
},
{
"cell_type": "code",
"execution_count": 10,
"metadata": {},
"outputs": [],
"source": [
"emb = keras.layers.Embedding(vocab_size,embed_size,weights=[W],trainable=False)\n",
"model = keras.models.Sequential([\n",
" vectorizer, emb,\n",
" keras.layers.Lambda(lambda x: tf.reduce_mean(x,axis=1)),\n",
" keras.layers.Dense(4, activation='softmax')\n",
"])"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": []
},
{
"cell_type": "code",
"execution_count": 11,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"938/938 [==============================] - 10s 10ms/step - loss: 1.1075 - acc: 0.7822 - val_loss: 0.9134 - val_acc: 0.8175\n"
]
},
{
"data": {
"text/plain": [
"<keras.callbacks.History at 0x2220226ef10>"
]
},
"execution_count": 11,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"model.compile(loss='sparse_categorical_crossentropy',metrics=['acc'])\n",
"model.fit(ds_train.map(tupelize).batch(batch_size),\n",
" validation_data=ds_test.map(tupelize).batch(batch_size))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"> **Notă**: Observați că am setat `trainable=False` atunci când am creat `Embedding`, ceea ce înseamnă că nu reantrenăm stratul Embedding. Acest lucru poate duce la o acuratețe ușor mai scăzută, dar accelerează procesul de antrenare.\n",
"\n",
"### Utilizarea vocabularului embedding\n",
"\n",
"O problemă cu abordarea anterioară este că vocabularele utilizate în TextVectorization și Embedding sunt diferite. Pentru a depăși această problemă, putem folosi una dintre următoarele soluții:\n",
"* Reantrenarea modelului Word2Vec pe vocabularul nostru.\n",
"* Încărcarea dataset-ului nostru folosind vocabularul din modelul Word2Vec preantrenat. Vocabularele utilizate pentru încărcarea dataset-ului pot fi specificate în timpul încărcării.\n",
"\n",
"A doua abordare pare mai simplă, așa că haideți să o implementăm. În primul rând, vom crea un strat `TextVectorization` cu vocabularul specificat, preluat din embedding-urile Word2Vec:\n"
]
},
{
"cell_type": "code",
"execution_count": 12,
"metadata": {},
"outputs": [],
"source": [
"vocab = list(w2v.vocab.keys())\n",
"vectorizer = keras.layers.experimental.preprocessing.TextVectorization(input_shape=(1,))\n",
"vectorizer.set_vocabulary(vocab)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Biblioteca de încorporări de cuvinte gensim conține o funcție convenabilă, `get_keras_embeddings`, care va crea automat stratul de încorporări Keras corespunzător pentru tine.\n"
]
},
{
"cell_type": "code",
"execution_count": 13,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Epoch 1/5\n",
"938/938 [==============================] - 20s 14ms/step - loss: 1.3377 - acc: 0.4978 - val_loss: 1.2995 - val_acc: 0.5647\n",
"Epoch 2/5\n",
"938/938 [==============================] - 10s 10ms/step - loss: 1.2587 - acc: 0.5722 - val_loss: 1.2339 - val_acc: 0.5842\n",
"Epoch 3/5\n",
"938/938 [==============================] - 10s 10ms/step - loss: 1.1980 - acc: 0.5884 - val_loss: 1.1826 - val_acc: 0.5954\n",
"Epoch 4/5\n",
"938/938 [==============================] - 12s 13ms/step - loss: 1.1503 - acc: 0.6002 - val_loss: 1.1417 - val_acc: 0.6018\n",
"Epoch 5/5\n",
"938/938 [==============================] - 11s 12ms/step - loss: 1.1120 - acc: 0.6097 - val_loss: 1.1083 - val_acc: 0.6104\n"
]
},
{
"data": {
"text/plain": [
"<keras.callbacks.History at 0x2220ccb81c0>"
]
},
"execution_count": 13,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"model = keras.models.Sequential([\n",
" vectorizer, \n",
" w2v.get_keras_embedding(train_embeddings=False),\n",
" keras.layers.Lambda(lambda x: tf.reduce_mean(x,axis=1)),\n",
" keras.layers.Dense(4, activation='softmax')\n",
"])\n",
"model.compile(loss='sparse_categorical_crossentropy',metrics=['acc'])\n",
"model.fit(ds_train.map(tupelize).batch(128),validation_data=ds_test.map(tupelize).batch(128),epochs=5)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Unul dintre motivele pentru care nu observăm o acuratețe mai mare este faptul că unele cuvinte din setul nostru de date lipsesc din vocabularul GloVe preantrenat și, astfel, sunt practic ignorate. Pentru a depăși acest obstacol, putem antrena propriile noastre încorporări bazate pe setul nostru de date.\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Încapsulări contextuale\n",
"\n",
"O limitare importantă a reprezentărilor tradiționale de încapsulare pre-antrenate, cum ar fi Word2Vec, este faptul că, deși pot surprinde o parte din sensul unui cuvânt, nu pot face diferența între sensurile diferite. Acest lucru poate cauza probleme în modelele ulterioare.\n",
"\n",
"De exemplu, cuvântul „play” are sensuri diferite în aceste două propoziții:\n",
"- Am fost la o **piesă** de teatru.\n",
"- John vrea să se **joace** cu prietenii săi.\n",
"\n",
"Încapsulările pre-antrenate despre care am vorbit reprezintă ambele sensuri ale cuvântului „play” în aceeași încapsulare. Pentru a depăși această limitare, trebuie să construim încapsulări bazate pe **modelul lingvistic**, care este antrenat pe un corpus mare de text și *știe* cum pot fi utilizate cuvintele în contexte diferite. Discutarea încapsulărilor contextuale depășește scopul acestui tutorial, dar vom reveni la ele când vom vorbi despre modelele lingvistice în unitatea următoare.\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"\n---\n\n**Declinare de responsabilitate**: \nAcest document a fost tradus folosind serviciul de traducere AI [Co-op Translator](https://github.com/Azure/co-op-translator). Deși ne străduim să asigurăm acuratețea, vă rugăm să fiți conștienți că traducerile automate pot conține erori sau inexactități. Documentul original în limba sa natală ar trebui considerat sursa autoritară. Pentru informații critice, se recomandă traducerea profesională realizată de un specialist uman. Nu ne asumăm responsabilitatea pentru eventualele neînțelegeri sau interpretări greșite care pot apărea din utilizarea acestei traduceri.\n"
]
}
],
"metadata": {
"interpreter": {
"hash": "0cb620c6d4b9f7a635928804c26cf22403d89d98d79684e4529119355ee6d5a5"
},
"kernel_info": {
"name": "conda-env-py37_tensorflow-py"
},
"kernelspec": {
"display_name": "py37_tensorflow",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.8.12"
},
"nteract": {
"version": "nteract-front-end@1.0.0"
},
"coopTranslator": {
"original_hash": "b859482be7f61d1eadc2c6a2720a37e4",
"translation_date": "2025-08-30T01:03:30+00:00",
"source_file": "lessons/5-NLP/14-Embeddings/EmbeddingsTF.ipynb",
"language_code": "ro"
}
},
"nbformat": 4,
"nbformat_minor": 4
}

View File

@ -0,0 +1,576 @@
{
"cells": [
{
"cell_type": "markdown",
"metadata": {
"id": "NXTSugt6ieXh"
},
"source": [
"## Antrenarea modelului CBoW\n",
"\n",
"Acest notebook face parte din [Curriculum AI pentru Începători](http://aka.ms/ai-beginners)\n",
"\n",
"În acest exemplu, vom analiza antrenarea unui model de limbaj CBoW pentru a obține propriul nostru spațiu de încorporare Word2Vec. Vom folosi setul de date AG News ca sursă de text.\n"
]
},
{
"cell_type": "code",
"source": [
"import torch\n",
"import torchtext\n",
"import os\n",
"import collections\n",
"import builtins\n",
"import random\n",
"import numpy as np"
],
"metadata": {
"id": "q-UiiJUKaxHj"
},
"execution_count": null,
"outputs": []
},
{
"cell_type": "code",
"source": [
"device = torch.device(\"cuda\" if torch.cuda.is_available() else \"cpu\")"
],
"metadata": {
"id": "TFbR8CZaTZ1q"
},
"execution_count": null,
"outputs": []
},
{
"cell_type": "markdown",
"source": [
"Mai întâi să încărcăm setul nostru de date și să definim tokenizer-ul și vocabularul. Vom seta `vocab_size` la 5000 pentru a limita puțin calculele.\n"
],
"metadata": {
"id": "HIwC7lI5T-ov"
}
},
{
"cell_type": "code",
"source": [
"def load_dataset(ngrams = 1, min_freq = 1, vocab_size = 5000 , lines_cnt = 500):\n",
" tokenizer = torchtext.data.utils.get_tokenizer('basic_english')\n",
" print(\"Loading dataset...\")\n",
" test_dataset, train_dataset = torchtext.datasets.AG_NEWS(root='./data')\n",
" train_dataset = list(train_dataset)\n",
" test_dataset = list(test_dataset)\n",
" classes = ['World', 'Sports', 'Business', 'Sci/Tech']\n",
" print('Building vocab...')\n",
" counter = collections.Counter()\n",
" for i, (_, line) in enumerate(train_dataset):\n",
" counter.update(torchtext.data.utils.ngrams_iterator(tokenizer(line),ngrams=ngrams))\n",
" if i == lines_cnt:\n",
" break\n",
" vocab = torchtext.vocab.Vocab(collections.Counter(dict(counter.most_common(vocab_size))), min_freq=min_freq)\n",
" return train_dataset, test_dataset, classes, vocab, tokenizer"
],
"metadata": {
"id": "wdZuygtgiuLG"
},
"execution_count": null,
"outputs": []
},
{
"cell_type": "code",
"source": [
"train_dataset, test_dataset, _, vocab, tokenizer = load_dataset()"
],
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/"
},
"id": "4d1nU1gsivGu",
"outputId": "949fe272-ae0e-49f5-c373-6703458b3a74"
},
"execution_count": null,
"outputs": [
{
"output_type": "stream",
"name": "stdout",
"text": [
"Loading dataset...\n",
"Building vocab...\n"
]
}
]
},
{
"cell_type": "code",
"source": [
"def encode(x, vocabulary, tokenizer = tokenizer):\n",
" return [vocabulary[s] for s in tokenizer(x)]"
],
"metadata": {
"id": "1XDYNhG8ToFV"
},
"execution_count": null,
"outputs": []
},
{
"cell_type": "markdown",
"metadata": {
"id": "LIlQk6_PaHVY"
},
"source": [
"## Modelul CBoW\n",
"\n",
"CBoW învață să prezică un cuvânt pe baza celor $2N$ cuvinte vecine. De exemplu, când $N=1$, vom obține următoarele perechi din propoziția *Îmi place să antrenez rețele*: (place,Îmi), (Îmi, place), (să, place), (place,să), (antrenez,să), (să, antrenez), (rețele, antrenez), (antrenez,rețele). Aici, primul cuvânt este cuvântul vecin folosit ca intrare, iar al doilea cuvânt este cel pe care îl prezicem.\n",
"\n",
"Pentru a construi o rețea care să prezică următorul cuvânt, va trebui să furnizăm cuvântul vecin ca intrare și să obținem numărul cuvântului ca ieșire. Arhitectura rețelei CBoW este următoarea:\n",
"\n",
"* Cuvântul de intrare este trecut prin stratul de încorporare. Acest strat de încorporare va fi chiar încorporarea Word2Vec, astfel încât îl vom defini separat ca variabilă `embedder`. Vom folosi o dimensiune de încorporare = 30 în acest exemplu, deși s-ar putea să doriți să experimentați cu dimensiuni mai mari (Word2Vec real are 300).\n",
"* Vectorul de încorporare va fi apoi trecut printr-un strat liniar care va prezice cuvântul de ieșire. Astfel, acesta are `vocab_size` neuroni.\n",
"\n",
"Pentru ieșire, dacă folosim `CrossEntropyLoss` ca funcție de pierdere, va trebui să furnizăm doar numerele cuvintelor ca rezultate așteptate, fără codificare one-hot.\n"
]
},
{
"cell_type": "code",
"source": [
"vocab_size = len(vocab)\n",
"\n",
"embedder = torch.nn.Embedding(num_embeddings = vocab_size, embedding_dim = 30)\n",
"model = torch.nn.Sequential(\n",
" embedder,\n",
" torch.nn.Linear(in_features = 30, out_features = vocab_size),\n",
")\n",
"\n",
"print(model)"
],
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/"
},
"id": "akKTcKQKkfl2",
"outputId": "da687e3e-a8ec-4c1a-e456-ab8cd6ac7dad"
},
"execution_count": null,
"outputs": [
{
"output_type": "stream",
"name": "stdout",
"text": [
"Sequential(\n",
" (0): Embedding(5002, 30)\n",
" (1): Linear(in_features=30, out_features=5002, bias=True)\n",
")\n"
]
}
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "Nud6jgGPaHVa"
},
"source": [
"## Pregătirea datelor de antrenament\n",
"\n",
"Acum să programăm funcția principală care va calcula perechile de cuvinte CBoW din text. Această funcție ne va permite să specificăm dimensiunea ferestrei și va returna un set de perechi - cuvânt de intrare și cuvânt de ieșire. Observați că această funcție poate fi utilizată atât pe cuvinte, cât și pe vectori/tensori - ceea ce ne va permite să codificăm textul înainte de a-l transmite funcției `to_cbow`.\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/"
},
"id": "x-dsXygOieXn",
"outputId": "c2218280-e540-40ba-9546-efe48d0d714f"
},
"outputs": [
{
"output_type": "stream",
"name": "stdout",
"text": [
"[['like', 'I'], ['to', 'I'], ['I', 'like'], ['to', 'like'], ['train', 'like'], ['I', 'to'], ['like', 'to'], ['train', 'to'], ['networks', 'to'], ['like', 'train'], ['to', 'train'], ['networks', 'train'], ['to', 'networks'], ['train', 'networks']]\n",
"[[232, 172], [5, 172], [172, 232], [5, 232], [0, 232], [172, 5], [232, 5], [0, 5], [1202, 5], [232, 0], [5, 0], [1202, 0], [5, 1202], [0, 1202]]\n"
]
}
],
"source": [
"def to_cbow(sent,window_size=2):\n",
" res = []\n",
" for i,x in enumerate(sent):\n",
" for j in range(max(0,i-window_size),min(i+window_size+1,len(sent))):\n",
" if i!=j:\n",
" res.append([sent[j],x])\n",
" return res\n",
"\n",
"print(to_cbow(['I','like','to','train','networks']))\n",
"print(to_cbow(encode('I like to train networks', vocab)))"
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "XVaaDLjaaHVb"
},
"source": [
"Să pregătim setul de date pentru antrenament. Vom parcurge toate știrile, vom apela `to_cbow` pentru a obține lista de perechi de cuvinte și vom adăuga aceste perechi în `X` și `Y`. Pentru a economisi timp, vom lua în considerare doar primele 10.000 de știri - puteți elimina cu ușurință această limitare dacă aveți mai mult timp la dispoziție și doriți să obțineți încorporări mai bune :)\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"id": "54b-Gd9TieXo"
},
"outputs": [],
"source": [
"X = []\n",
"Y = []\n",
"for i, x in zip(range(10000), train_dataset):\n",
" for w1, w2 in to_cbow(encode(x[1], vocab), window_size = 5):\n",
" X.append(w1)\n",
" Y.append(w2)\n",
"\n",
"X = torch.tensor(X)\n",
"Y = torch.tensor(Y)"
]
},
{
"cell_type": "markdown",
"source": [
"Vom converti, de asemenea, acele date într-un singur set de date și vom crea un dataloader:\n"
],
"metadata": {
"id": "cwWy0PzXWhN5"
}
},
{
"cell_type": "code",
"source": [
"class SimpleIterableDataset(torch.utils.data.IterableDataset):\n",
" def __init__(self, X, Y):\n",
" super(SimpleIterableDataset).__init__()\n",
" self.data = []\n",
" for i in range(len(X)):\n",
" self.data.append( (Y[i], X[i]) )\n",
" random.shuffle(self.data)\n",
"\n",
" def __iter__(self):\n",
" return iter(self.data)"
],
"metadata": {
"id": "mfoAcGPFZU8p"
},
"execution_count": null,
"outputs": []
},
{
"cell_type": "markdown",
"metadata": {
"id": "e4NQ_-5waHVc"
},
"source": [
"Vom converti, de asemenea, acele date într-un singur set de date și vom crea un dataloader:\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"id": "AbLUcojlieXo"
},
"outputs": [],
"source": [
"ds = SimpleIterableDataset(X, Y)\n",
"dl = torch.utils.data.DataLoader(ds, batch_size = 256)"
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "pKQr7sXeaHVc"
},
"source": [
"Acum să trecem la antrenamentul propriu-zis. Vom folosi optimizatorul `SGD` cu o rată de învățare destul de mare. De asemenea, poți încerca să experimentezi cu alți optimizatori, cum ar fi `Adam`. Vom antrena pentru 10 epoci la început - și poți rula din nou această celulă dacă dorești o pierdere și mai mică.\n"
]
},
{
"cell_type": "code",
"source": [
"def train_epoch(net, dataloader, lr = 0.01, optimizer = None, loss_fn = torch.nn.CrossEntropyLoss(), epochs = None, report_freq = 1):\n",
" optimizer = optimizer or torch.optim.Adam(net.parameters(), lr = lr)\n",
" loss_fn = loss_fn.to(device)\n",
" net.train()\n",
"\n",
" for i in range(epochs):\n",
" total_loss, j = 0, 0, \n",
" for labels, features in dataloader:\n",
" optimizer.zero_grad()\n",
" features, labels = features.to(device), labels.to(device)\n",
" out = net(features)\n",
" loss = loss_fn(out, labels)\n",
" loss.backward()\n",
" optimizer.step()\n",
" total_loss += loss\n",
" j += 1\n",
" if i % report_freq == 0:\n",
" print(f\"Epoch: {i+1}: loss={total_loss.item()/j}\")\n",
"\n",
" return total_loss.item()/j"
],
"metadata": {
"id": "HeeCYKr_KF1w"
},
"execution_count": null,
"outputs": []
},
{
"cell_type": "code",
"source": [
"train_epoch(net = model, dataloader = dl, optimizer = torch.optim.SGD(model.parameters(), lr = 0.1), loss_fn = torch.nn.CrossEntropyLoss(), epochs = 10)"
],
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/"
},
"id": "KVgwGtDHgDlT",
"outputId": "2447833f-f0e3-4566-c33d-addbfe2f451d"
},
"execution_count": null,
"outputs": [
{
"output_type": "stream",
"name": "stdout",
"text": [
"Epoch: 1: loss=5.664632366860172\n",
"Epoch: 2: loss=5.632101973960962\n",
"Epoch: 3: loss=5.610399051405015\n",
"Epoch: 4: loss=5.594621561080262\n",
"Epoch: 5: loss=5.582538017415446\n",
"Epoch: 6: loss=5.572900234519603\n",
"Epoch: 7: loss=5.564951676341915\n",
"Epoch: 8: loss=5.558288112064614\n",
"Epoch: 9: loss=5.552576955031129\n",
"Epoch: 10: loss=5.547634165194347\n"
]
},
{
"output_type": "execute_result",
"data": {
"text/plain": [
"5.547634165194347"
]
},
"metadata": {},
"execution_count": 16
}
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "W8u2qXZmaHVd"
},
"source": [
"## Testarea Word2Vec\n",
"\n",
"Pentru a utiliza Word2Vec, să extragem vectorii corespunzători tuturor cuvintelor din vocabularul nostru:\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"id": "r8TatcXjkU_t"
},
"outputs": [],
"source": [
"vectors = torch.stack([embedder(torch.tensor(vocab[s])) for s in vocab.itos], 0)"
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "3OcX21UOaHVd"
},
"source": [
"Să vedem, de exemplu, cum cuvântul **Paris** este codificat într-un vector:\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/"
},
"id": "bz6tAeLzieXp",
"outputId": "5b20850e-4342-45e9-f840-cfac2b4d61d8"
},
"outputs": [
{
"output_type": "stream",
"name": "stdout",
"text": [
"tensor([-0.0915, 2.1224, -0.0281, -0.6819, 1.1219, 0.6458, -1.3704, -1.3314,\n",
" -1.1437, 0.4496, 0.2301, -0.3515, -0.8485, 1.0481, 0.4386, -0.8949,\n",
" 0.5644, 1.0939, -2.5096, 3.2949, -0.2601, -0.8640, 0.1421, -0.0804,\n",
" -0.5083, -1.0560, 0.9753, -0.5949, -1.6046, 0.5774],\n",
" grad_fn=<EmbeddingBackward>)\n"
]
}
],
"source": [
"paris_vec = embedder(torch.tensor(vocab['paris']))\n",
"print(paris_vec)"
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "pHTJlaeYaHVd"
},
"source": [
"Este interesant să folosești Word2Vec pentru a căuta sinonime. Funcția următoare va returna `n` cele mai apropiate cuvinte față de un input dat. Pentru a le găsi, calculăm norma $|w_i - v|$, unde $v$ este vectorul corespunzător cuvântului nostru de intrare, iar $w_i$ este codificarea celui de-al `i`-lea cuvânt din vocabular. Apoi sortăm array-ul și returnăm indicii corespunzători folosind `argsort`, și luăm primele `n` elemente din listă, care codifică pozițiile celor mai apropiate cuvinte din vocabular.\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/"
},
"id": "NlZyi-_olFar",
"outputId": "b5dbb163-88c4-4d5a-eaf2-6751f700e98c"
},
"outputs": [
{
"output_type": "execute_result",
"data": {
"text/plain": [
"['microsoft', 'quoted', 'lp', 'rate', 'top']"
]
},
"metadata": {},
"execution_count": 56
}
],
"source": [
"def close_words(x, n = 5):\n",
" vec = embedder(torch.tensor(vocab[x]))\n",
" top5 = np.linalg.norm(vectors.detach().numpy() - vec.detach().numpy(), axis = 1).argsort()[:n]\n",
" return [ vocab.itos[x] for x in top5 ]\n",
"\n",
"close_words('microsoft')"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/"
},
"id": "-dQq7xeAln0U",
"outputId": "66f768c3-c248-4bfd-ce4f-c8ffc6d0dd0d"
},
"outputs": [
{
"output_type": "execute_result",
"data": {
"text/plain": [
"['basketball', 'lot', 'sinai', 'states', 'healthdaynews']"
]
},
"metadata": {},
"execution_count": 51
}
],
"source": [
"close_words('basketball')"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/"
},
"id": "fJXqK26b29sa",
"outputId": "78f0baba-ffd0-485a-dd87-0a12bedfd7fa"
},
"outputs": [
{
"output_type": "execute_result",
"data": {
"text/plain": [
"['funds', 'travel', 'sydney', 'japan', 'business']"
]
},
"metadata": {},
"execution_count": 77
}
],
"source": [
"close_words('funds')"
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "My0VeTDd3Ji8"
},
"source": [
"## Concluzie\n",
"\n",
"Folosind tehnici ingenioase precum CBoW, putem antrena modelul Word2Vec. De asemenea, poți încerca să antrenezi modelul skip-gram, care este conceput să prezică cuvântul vecin având în vedere cel central, și să vezi cât de bine funcționează.\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"\n---\n\n**Declinare de responsabilitate**: \nAcest document a fost tradus folosind serviciul de traducere AI [Co-op Translator](https://github.com/Azure/co-op-translator). Deși ne străduim să asigurăm acuratețea, vă rugăm să rețineți că traducerile automate pot conține erori sau inexactități. Documentul original în limba sa natală ar trebui considerat sursa autoritară. Pentru informații critice, se recomandă traducerea profesională realizată de un specialist uman. Nu ne asumăm responsabilitatea pentru eventualele neînțelegeri sau interpretări greșite care pot apărea din utilizarea acestei traduceri.\n"
]
}
],
"metadata": {
"colab": {
"collapsed_sections": [],
"name": "CBoW-PyTorch.ipynb",
"provenance": []
},
"interpreter": {
"hash": "16af2a8bbb083ea23e5e41c7f5787656b2ce26968575d8763f2c4b17f9cd711f"
},
"kernelspec": {
"display_name": "Python 3.8.12 ('py38')",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.8.12"
},
"orig_nbformat": 4,
"gpuClass": "standard",
"coopTranslator": {
"original_hash": "36df28efe3fe40b6fb0a7fa48fe3ea82",
"translation_date": "2025-08-30T00:40:02+00:00",
"source_file": "lessons/5-NLP/15-LanguageModeling/CBoW-PyTorch.ipynb",
"language_code": "ro"
}
},
"nbformat": 4,
"nbformat_minor": 0
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,479 @@
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Rețele neuronale recurente\n",
"\n",
"În modulul anterior, am utilizat reprezentări semantice bogate ale textului și un clasificator liniar simplu deasupra acestor încorporări. Ceea ce face această arhitectură este să capteze semnificația agregată a cuvintelor dintr-o propoziție, dar nu ia în considerare **ordinea** cuvintelor, deoarece operația de agregare aplicată încorporărilor elimină această informație din textul original. Deoarece aceste modele nu pot modela ordonarea cuvintelor, ele nu pot rezolva sarcini mai complexe sau ambigue, cum ar fi generarea de text sau răspunsul la întrebări.\n",
"\n",
"Pentru a capta semnificația unei secvențe de text, trebuie să utilizăm o altă arhitectură de rețea neuronală, numită **rețea neuronală recurentă**, sau RNN. În RNN, trecem propoziția prin rețea, simbol cu simbol, iar rețeaua produce un anumit **stat**, pe care îl trecem din nou prin rețea împreună cu următorul simbol.\n",
"\n",
"Dată fiind secvența de tokeni $X_0,\\dots,X_n$, RNN creează o secvență de blocuri de rețea neuronală și antrenează această secvență cap-coadă folosind propagarea înapoi. Fiecare bloc de rețea primește o pereche $(X_i,S_i)$ ca intrare și produce $S_{i+1}$ ca rezultat. Starea finală $S_n$ sau ieșirea $X_n$ este transmisă unui clasificator liniar pentru a produce rezultatul. Toate blocurile de rețea împărtășesc aceleași greutăți și sunt antrenate cap-coadă folosind o singură trecere de propagare înapoi.\n",
"\n",
"Deoarece vectorii de stare $S_0,\\dots,S_n$ sunt trecuți prin rețea, aceasta este capabilă să învețe dependențele secvențiale dintre cuvinte. De exemplu, atunci când cuvântul *not* apare undeva în secvență, rețeaua poate învăța să nege anumite elemente din vectorul de stare, rezultând o negare.\n",
"\n",
"> Deoarece greutățile tuturor blocurilor RNN din imagine sunt împărtășite, aceeași imagine poate fi reprezentată ca un singur bloc (în dreapta) cu o buclă de feedback recurent, care transmite starea de ieșire a rețelei înapoi la intrare.\n",
"\n",
"Să vedem cum rețelele neuronale recurente ne pot ajuta să clasificăm setul nostru de date de știri.\n"
]
},
{
"cell_type": "code",
"execution_count": 1,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Loading dataset...\n",
"Building vocab...\n"
]
}
],
"source": [
"import torch\n",
"import torchtext\n",
"from torchnlp import *\n",
"train_dataset, test_dataset, classes, vocab = load_dataset()\n",
"vocab_size = len(vocab)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Clasificator RNN simplu\n",
"\n",
"În cazul unui RNN simplu, fiecare unitate recurentă este o rețea liniară simplă, care primește un vector de intrare concatenat și un vector de stare, și produce un nou vector de stare. PyTorch reprezintă această unitate prin clasa `RNNCell`, iar o rețea formată din astfel de celule - prin stratul `RNN`.\n",
"\n",
"Pentru a defini un clasificator RNN, vom aplica mai întâi un strat de încorporare pentru a reduce dimensiunea vocabularului de intrare, iar apoi vom adăuga un strat RNN deasupra acestuia:\n"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {},
"outputs": [],
"source": [
"class RNNClassifier(torch.nn.Module):\n",
" def __init__(self, vocab_size, embed_dim, hidden_dim, num_class):\n",
" super().__init__()\n",
" self.hidden_dim = hidden_dim\n",
" self.embedding = torch.nn.Embedding(vocab_size, embed_dim)\n",
" self.rnn = torch.nn.RNN(embed_dim,hidden_dim,batch_first=True)\n",
" self.fc = torch.nn.Linear(hidden_dim, num_class)\n",
"\n",
" def forward(self, x):\n",
" batch_size = x.size(0)\n",
" x = self.embedding(x)\n",
" x,h = self.rnn(x)\n",
" return self.fc(x.mean(dim=1))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"> **Note:** Folosim un strat de încorporare neantrenat aici pentru simplitate, dar pentru rezultate și mai bune putem folosi un strat de încorporare pre-antrenat cu încorporări Word2Vec sau GloVe, așa cum este descris în unitatea anterioară. Pentru o înțelegere mai bună, s-ar putea să doriți să adaptați acest cod pentru a funcționa cu încorporări pre-antrenate.\n",
"\n",
"În cazul nostru, vom folosi un loader de date cu secvențe completate, astfel încât fiecare lot va avea un număr de secvențe completate de aceeași lungime. Stratul RNN va lua secvența de tensori de încorporare și va produce două ieșiri:\n",
"* $x$ este o secvență de ieșiri ale celulelor RNN la fiecare pas\n",
"* $h$ este starea ascunsă finală pentru ultimul element al secvenței\n",
"\n",
"Aplicăm apoi un clasificator liniar complet conectat pentru a obține numărul de clase.\n",
"\n",
"> **Note:** RNN-urile sunt destul de dificile de antrenat, deoarece odată ce celulele RNN sunt desfășurate de-a lungul lungimii secvenței, numărul rezultat de straturi implicate în propagarea înapoi este destul de mare. Astfel, trebuie să selectăm o rată de învățare mică și să antrenăm rețeaua pe un set de date mai mare pentru a obține rezultate bune. Poate dura destul de mult timp, așa că utilizarea GPU-ului este preferată.\n"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {
"scrolled": true
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"3200: acc=0.3090625\n",
"6400: acc=0.38921875\n",
"9600: acc=0.4590625\n",
"12800: acc=0.511953125\n",
"16000: acc=0.5506875\n",
"19200: acc=0.57921875\n",
"22400: acc=0.6070089285714285\n",
"25600: acc=0.6304296875\n",
"28800: acc=0.6484027777777778\n",
"32000: acc=0.66509375\n",
"35200: acc=0.6790056818181818\n",
"38400: acc=0.6929166666666666\n",
"41600: acc=0.7035817307692308\n",
"44800: acc=0.7137276785714286\n",
"48000: acc=0.72225\n",
"51200: acc=0.73001953125\n",
"54400: acc=0.7372794117647059\n",
"57600: acc=0.7436631944444444\n",
"60800: acc=0.7503947368421052\n",
"64000: acc=0.75634375\n",
"67200: acc=0.7615773809523809\n",
"70400: acc=0.7662642045454545\n",
"73600: acc=0.7708423913043478\n",
"76800: acc=0.7751822916666666\n",
"80000: acc=0.7790625\n",
"83200: acc=0.7825\n",
"86400: acc=0.7858564814814815\n",
"89600: acc=0.7890513392857142\n",
"92800: acc=0.7920474137931034\n",
"96000: acc=0.7952708333333334\n",
"99200: acc=0.7982258064516129\n",
"102400: acc=0.80099609375\n",
"105600: acc=0.8037594696969697\n",
"108800: acc=0.8060569852941176\n"
]
}
],
"source": [
"train_loader = torch.utils.data.DataLoader(train_dataset, batch_size=16, collate_fn=padify, shuffle=True)\n",
"net = RNNClassifier(vocab_size,64,32,len(classes)).to(device)\n",
"train_epoch(net,train_loader, lr=0.001)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Memorie pe Termen Lung și Scurt (LSTM)\n",
"\n",
"Una dintre principalele probleme ale RNN-urilor clasice este așa-numita problemă a **gradientului care dispare**. Deoarece RNN-urile sunt antrenate cap-coadă într-o singură trecere de back-propagation, acestea întâmpină dificultăți în propagarea erorii către primele straturi ale rețelei, iar astfel rețeaua nu poate învăța relațiile dintre tokenii distanți. Una dintre modalitățile de a evita această problemă este introducerea **gestionării explicite a stării** prin utilizarea așa-numitelor **porți**. Există două arhitecturi bine cunoscute de acest tip: **Memorie pe Termen Lung și Scurt** (LSTM) și **Unitatea de Releu cu Porți** (GRU).\n",
"\n",
"![Imagine care arată un exemplu de celulă LSTM](../../../../../lessons/5-NLP/16-RNN/images/long-short-term-memory-cell.svg)\n",
"\n",
"Rețeaua LSTM este organizată într-un mod similar cu RNN, dar există două stări care sunt transmise de la un strat la altul: starea actuală $c$ și vectorul ascuns $h$. La fiecare unitate, vectorul ascuns $h_i$ este concatenat cu intrarea $x_i$, iar acestea controlează ce se întâmplă cu starea $c$ prin intermediul **porților**. Fiecare poartă este o rețea neuronală cu activare sigmoidă (ieșire în intervalul $[0,1]$), care poate fi considerată ca o mască bit cu bit atunci când este înmulțită cu vectorul de stare. Există următoarele porți (de la stânga la dreapta în imaginea de mai sus):\n",
"* **Poarta de uitare** preia vectorul ascuns și determină ce componente ale vectorului $c$ trebuie să uităm și care să fie transmise mai departe.\n",
"* **Poarta de intrare** preia informații din intrare și din vectorul ascuns și le inserează în stare.\n",
"* **Poarta de ieșire** transformă starea printr-un strat liniar cu activare $\\tanh$, apoi selectează unele dintre componentele sale folosind vectorul ascuns $h_i$ pentru a produce noua stare $c_{i+1}$.\n",
"\n",
"Componentele stării $c$ pot fi considerate ca niște semnale care pot fi activate sau dezactivate. De exemplu, când întâlnim un nume precum *Alice* într-o secvență, putem presupune că se referă la un personaj feminin și putem activa semnalul în stare care indică faptul că avem un substantiv feminin în propoziție. Când întâlnim ulterior expresia *și Tom*, vom activa semnalul care indică faptul că avem un substantiv la plural. Astfel, prin manipularea stării, putem, teoretic, să urmărim proprietățile gramaticale ale părților propoziției.\n",
"\n",
"> **Note**: O resursă excelentă pentru a înțelege detaliile interne ale LSTM este acest articol minunat [Understanding LSTM Networks](https://colah.github.io/posts/2015-08-Understanding-LSTMs/) de Christopher Olah.\n",
"\n",
"Deși structura internă a celulei LSTM poate părea complexă, PyTorch ascunde această implementare în clasa `LSTMCell` și oferă obiectul `LSTM` pentru a reprezenta întregul strat LSTM. Astfel, implementarea unui clasificator LSTM va fi destul de similară cu cea a unui RNN simplu pe care l-am văzut mai sus:\n"
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {},
"outputs": [],
"source": [
"class LSTMClassifier(torch.nn.Module):\n",
" def __init__(self, vocab_size, embed_dim, hidden_dim, num_class):\n",
" super().__init__()\n",
" self.hidden_dim = hidden_dim\n",
" self.embedding = torch.nn.Embedding(vocab_size, embed_dim)\n",
" self.embedding.weight.data = torch.randn_like(self.embedding.weight.data)-0.5\n",
" self.rnn = torch.nn.LSTM(embed_dim,hidden_dim,batch_first=True)\n",
" self.fc = torch.nn.Linear(hidden_dim, num_class)\n",
"\n",
" def forward(self, x):\n",
" batch_size = x.size(0)\n",
" x = self.embedding(x)\n",
" x,(h,c) = self.rnn(x)\n",
" return self.fc(h[-1])"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": []
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"3200: acc=0.259375\n",
"6400: acc=0.25859375\n",
"9600: acc=0.26177083333333334\n",
"12800: acc=0.2784375\n",
"16000: acc=0.313\n",
"19200: acc=0.3528645833333333\n",
"22400: acc=0.3965625\n",
"25600: acc=0.4385546875\n",
"28800: acc=0.4752777777777778\n",
"32000: acc=0.505375\n",
"35200: acc=0.5326704545454546\n",
"38400: acc=0.5557552083333334\n",
"41600: acc=0.5760817307692307\n",
"44800: acc=0.5954910714285714\n",
"48000: acc=0.6118333333333333\n",
"51200: acc=0.62681640625\n",
"54400: acc=0.6404779411764706\n",
"57600: acc=0.6520138888888889\n",
"60800: acc=0.662828947368421\n",
"64000: acc=0.673546875\n",
"67200: acc=0.6831547619047619\n",
"70400: acc=0.6917897727272727\n",
"73600: acc=0.6997146739130434\n",
"76800: acc=0.707109375\n",
"80000: acc=0.714075\n",
"83200: acc=0.7209134615384616\n",
"86400: acc=0.727037037037037\n",
"89600: acc=0.7326674107142858\n",
"92800: acc=0.7379633620689655\n",
"96000: acc=0.7433645833333333\n",
"99200: acc=0.7479032258064516\n",
"102400: acc=0.752119140625\n",
"105600: acc=0.7562405303030303\n",
"108800: acc=0.76015625\n",
"112000: acc=0.7641339285714286\n",
"115200: acc=0.7677777777777778\n",
"118400: acc=0.7711233108108108\n"
]
},
{
"data": {
"text/plain": [
"(0.03487814127604167, 0.7728)"
]
},
"execution_count": 5,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"net = LSTMClassifier(vocab_size,64,32,len(classes)).to(device)\n",
"train_epoch(net,train_loader, lr=0.001)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Secvențe împachetate\n",
"\n",
"În exemplul nostru, a trebuit să completăm toate secvențele din minibatch cu vectori de zero. Deși acest lucru duce la o risipă de memorie, în cazul RNN-urilor este mai critic faptul că se creează celule RNN suplimentare pentru elementele de intrare completate, care participă la antrenament, dar nu conțin informații importante. Ar fi mult mai bine să antrenăm RNN doar pentru dimensiunea reală a secvenței.\n",
"\n",
"Pentru a face acest lucru, PyTorch introduce un format special de stocare a secvențelor completate. Să presupunem că avem un minibatch de intrare completat care arată astfel:\n",
"```\n",
"[[1,2,3,4,5],\n",
" [6,7,8,0,0],\n",
" [9,0,0,0,0]]\n",
"```\n",
"Aici, 0 reprezintă valorile completate, iar vectorul de lungime reală al secvențelor de intrare este `[5,3,1]`.\n",
"\n",
"Pentru a antrena eficient RNN cu secvențe completate, dorim să începem antrenamentul primului grup de celule RNN cu un minibatch mare (`[1,6,9]`), dar apoi să terminăm procesarea celei de-a treia secvențe și să continuăm antrenamentul cu minibatch-uri mai mici (`[2,7]`, `[3,8]`) și așa mai departe. Astfel, secvența împachetată este reprezentată ca un singur vector - în cazul nostru `[1,6,9,2,7,3,8,4,5]`, și un vector de lungime (`[5,3,1]`), din care putem reconstrui cu ușurință minibatch-ul completat original.\n",
"\n",
"Pentru a produce o secvență împachetată, putem folosi funcția `torch.nn.utils.rnn.pack_padded_sequence`. Toate straturile recurente, inclusiv RNN, LSTM și GRU, acceptă secvențe împachetate ca intrare și produc o ieșire împachetată, care poate fi decodificată folosind `torch.nn.utils.rnn.pad_packed_sequence`.\n",
"\n",
"Pentru a putea produce o secvență împachetată, trebuie să transmitem vectorul de lungime rețelei, și astfel avem nevoie de o funcție diferită pentru a pregăti minibatch-urile:\n"
]
},
{
"cell_type": "code",
"execution_count": 6,
"metadata": {},
"outputs": [],
"source": [
"def pad_length(b):\n",
" # build vectorized sequence\n",
" v = [encode(x[1]) for x in b]\n",
" # compute max length of a sequence in this minibatch and length sequence itself\n",
" len_seq = list(map(len,v))\n",
" l = max(len_seq)\n",
" return ( # tuple of three tensors - labels, padded features, length sequence\n",
" torch.LongTensor([t[0]-1 for t in b]),\n",
" torch.stack([torch.nn.functional.pad(torch.tensor(t),(0,l-len(t)),mode='constant',value=0) for t in v]),\n",
" torch.tensor(len_seq)\n",
" )\n",
"\n",
"train_loader_len = torch.utils.data.DataLoader(train_dataset, batch_size=16, collate_fn=pad_length, shuffle=True)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Rețeaua efectivă ar fi foarte similară cu `LSTMClassifier` de mai sus, dar trecerea `forward` va primi atât minibatch-ul umplut, cât și vectorul lungimilor secvențelor. După ce calculăm embedding-ul, calculăm secvența împachetată, o trecem prin stratul LSTM și apoi despachetăm rezultatul înapoi.\n",
"\n",
"> **Notă**: De fapt, nu folosim rezultatul despachetat `x`, deoarece utilizăm ieșirea din straturile ascunse în calculele următoare. Astfel, putem elimina complet despachetarea din acest cod. Motivul pentru care o plasăm aici este pentru a vă permite să modificați acest cod cu ușurință, în cazul în care aveți nevoie să utilizați ieșirea rețelei în calcule ulterioare.\n"
]
},
{
"cell_type": "code",
"execution_count": 7,
"metadata": {},
"outputs": [],
"source": [
"class LSTMPackClassifier(torch.nn.Module):\n",
" def __init__(self, vocab_size, embed_dim, hidden_dim, num_class):\n",
" super().__init__()\n",
" self.hidden_dim = hidden_dim\n",
" self.embedding = torch.nn.Embedding(vocab_size, embed_dim)\n",
" self.embedding.weight.data = torch.randn_like(self.embedding.weight.data)-0.5\n",
" self.rnn = torch.nn.LSTM(embed_dim,hidden_dim,batch_first=True)\n",
" self.fc = torch.nn.Linear(hidden_dim, num_class)\n",
"\n",
" def forward(self, x, lengths):\n",
" batch_size = x.size(0)\n",
" x = self.embedding(x)\n",
" pad_x = torch.nn.utils.rnn.pack_padded_sequence(x,lengths,batch_first=True,enforce_sorted=False)\n",
" pad_x,(h,c) = self.rnn(pad_x)\n",
" x, _ = torch.nn.utils.rnn.pad_packed_sequence(pad_x,batch_first=True)\n",
" return self.fc(h[-1])"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": []
},
{
"cell_type": "code",
"execution_count": 8,
"metadata": {
"scrolled": true
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"3200: acc=0.285625\n",
"6400: acc=0.33359375\n",
"9600: acc=0.3876041666666667\n",
"12800: acc=0.44078125\n",
"16000: acc=0.4825\n",
"19200: acc=0.5235416666666667\n",
"22400: acc=0.5559821428571429\n",
"25600: acc=0.58609375\n",
"28800: acc=0.6116666666666667\n",
"32000: acc=0.63340625\n",
"35200: acc=0.6525284090909091\n",
"38400: acc=0.668515625\n",
"41600: acc=0.6822596153846154\n",
"44800: acc=0.6948214285714286\n",
"48000: acc=0.7052708333333333\n",
"51200: acc=0.71521484375\n",
"54400: acc=0.7239889705882353\n",
"57600: acc=0.7315277777777778\n",
"60800: acc=0.7388486842105263\n",
"64000: acc=0.74571875\n",
"67200: acc=0.7518303571428572\n",
"70400: acc=0.7576988636363636\n",
"73600: acc=0.7628940217391305\n",
"76800: acc=0.7681510416666667\n",
"80000: acc=0.7728125\n",
"83200: acc=0.7772235576923077\n",
"86400: acc=0.7815393518518519\n",
"89600: acc=0.7857700892857142\n",
"92800: acc=0.7895043103448276\n",
"96000: acc=0.7930520833333333\n",
"99200: acc=0.7959072580645161\n",
"102400: acc=0.798994140625\n",
"105600: acc=0.802064393939394\n",
"108800: acc=0.8051378676470589\n",
"112000: acc=0.8077857142857143\n",
"115200: acc=0.8104600694444445\n",
"118400: acc=0.8128293918918919\n"
]
},
{
"data": {
"text/plain": [
"(0.029785829671223958, 0.8138166666666666)"
]
},
"execution_count": 8,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"net = LSTMPackClassifier(vocab_size,64,32,len(classes)).to(device)\n",
"train_epoch_emb(net,train_loader_len, lr=0.001,use_pack_sequence=True)\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"> **Notă:** Este posibil să fi observat parametrul `use_pack_sequence` pe care îl transmitem funcției de antrenare. În prezent, funcția `pack_padded_sequence` necesită ca tensorul de lungime a secvenței să fie pe dispozitivul CPU, și astfel funcția de antrenare trebuie să evite mutarea datelor de lungime a secvenței pe GPU în timpul antrenării. Puteți analiza implementarea funcției `train_emb` în fișierul [`torchnlp.py`](../../../../../lessons/5-NLP/16-RNN/torchnlp.py).\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## RNN-uri bidirecționale și multilayer\n",
"\n",
"În exemplele noastre, toate rețelele recurente au funcționat într-o singură direcție, de la începutul unei secvențe până la sfârșit. Pare natural, deoarece seamănă cu modul în care citim și ascultăm vorbirea. Totuși, în multe cazuri practice, avem acces aleatoriu la secvența de intrare, așa că ar putea avea sens să efectuăm calculul recurent în ambele direcții. Astfel de rețele se numesc **RNN-uri bidirecționale**, și pot fi create prin transmiterea parametrului `bidirectional=True` către constructorul RNN/LSTM/GRU.\n",
"\n",
"Când lucrăm cu o rețea bidirecțională, vom avea nevoie de două vectori de stare ascunsă, câte unul pentru fiecare direcție. PyTorch codifică acești vectori ca un singur vector de dimensiune dublă, ceea ce este destul de convenabil, deoarece, de obicei, vectorul de stare rezultat este transmis unui strat liniar complet conectat, și trebuie doar să ținem cont de această creștere în dimensiune atunci când creăm stratul.\n",
"\n",
"O rețea recurentă, fie unidirecțională, fie bidirecțională, captează anumite modele dintr-o secvență și le poate stoca în vectorul de stare sau le poate transmite în ieșire. La fel ca în cazul rețelelor convoluționale, putem construi un alt strat recurent deasupra primului pentru a capta modele de nivel superior, construite din modelele de nivel inferior extrase de primul strat. Acest lucru ne conduce la conceptul de **RNN multilayer**, care constă din două sau mai multe rețele recurente, unde ieșirea stratului anterior este transmisă stratului următor ca intrare.\n",
"\n",
"![Imagine care arată un RNN multilayer cu memorie pe termen lung și scurt](../../../../../translated_images/multi-layer-lstm.dd975e29bb2a59fe58b429db833932d734c81f211cad2783797a9608984acb8c.ro.jpg)\n",
"\n",
"*Imagine din [această postare minunată](https://towardsdatascience.com/from-a-lstm-cell-to-a-multilayer-lstm-network-with-pytorch-2899eb5696f3) de Fernando López*\n",
"\n",
"PyTorch face construirea unor astfel de rețele o sarcină ușoară, deoarece trebuie doar să transmiteți parametrul `num_layers` către constructorul RNN/LSTM/GRU pentru a construi automat mai multe straturi de recurență. Acest lucru înseamnă, de asemenea, că dimensiunea vectorului de stare ascunsă va crește proporțional, și va trebui să țineți cont de acest aspect atunci când gestionați ieșirea straturilor recurente.\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## RNN-uri pentru alte sarcini\n",
"\n",
"În această unitate, am văzut că RNN-urile pot fi utilizate pentru clasificarea secvențelor, dar, de fapt, ele pot gestiona multe alte sarcini, cum ar fi generarea de text, traducerea automată și altele. Vom analiza aceste sarcini în unitatea următoare.\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"\n---\n\n**Declinare de responsabilitate**: \nAcest document a fost tradus folosind serviciul de traducere AI [Co-op Translator](https://github.com/Azure/co-op-translator). Deși ne străduim să asigurăm acuratețea, vă rugăm să rețineți că traducerile automate pot conține erori sau inexactități. Documentul original în limba sa maternă ar trebui considerat sursa autoritară. Pentru informații critice, se recomandă traducerea profesională realizată de un specialist uman. Nu ne asumăm responsabilitatea pentru eventualele neînțelegeri sau interpretări greșite care pot apărea din utilizarea acestei traduceri.\n"
]
}
],
"metadata": {
"interpreter": {
"hash": "16af2a8bbb083ea23e5e41c7f5787656b2ce26968575d8763f2c4b17f9cd711f"
},
"kernelspec": {
"display_name": "Python 3.8.12 ('py38')",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.8.12"
},
"coopTranslator": {
"original_hash": "522ee52ae3d5ae933e283286254e9a55",
"translation_date": "2025-08-30T00:59:23+00:00",
"source_file": "lessons/5-NLP/16-RNN/RNNPyTorch.ipynb",
"language_code": "ro"
}
},
"nbformat": 4,
"nbformat_minor": 2
}

View File

@ -0,0 +1,460 @@
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Rețele neuronale recurente\n",
"\n",
"În modulul anterior, am discutat despre reprezentările semantice bogate ale textului. Arhitectura pe care am folosit-o captează semnificația agregată a cuvintelor dintr-o propoziție, dar nu ia în considerare **ordinea** cuvintelor, deoarece operația de agregare care urmează după încorporări elimină această informație din textul original. Deoarece aceste modele nu pot reprezenta ordonarea cuvintelor, ele nu pot rezolva sarcini mai complexe sau ambigue, cum ar fi generarea de text sau răspunsul la întrebări.\n",
"\n",
"Pentru a capta semnificația unei secvențe de text, vom folosi o arhitectură de rețea neuronală numită **rețea neuronală recurentă**, sau RNN. Când utilizăm o RNN, trecem propoziția prin rețea, un token pe rând, iar rețeaua produce un **statut**, pe care îl transmitem din nou rețelei împreună cu următorul token.\n",
"\n",
"![Imagine care arată un exemplu de generare a unei rețele neuronale recurente.](../../../../../translated_images/rnn.27f5c29c53d727b546ad3961637a267f0fe9ec5ab01f2a26a853c92fcefbb574.ro.png)\n",
"\n",
"Având secvența de intrare de tokeni $X_0,\\dots,X_n$, RNN creează o secvență de blocuri de rețea neuronală și antrenează această secvență cap-coadă folosind retropropagarea. Fiecare bloc de rețea ia o pereche $(X_i,S_i)$ ca intrare și produce $S_{i+1}$ ca rezultat. Statutul final $S_n$ sau ieșirea $Y_n$ este transmisă unui clasificator liniar pentru a produce rezultatul. Toate blocurile de rețea împărtășesc aceleași greutăți și sunt antrenate cap-coadă folosind o singură trecere de retropropagare.\n",
"\n",
"> Figura de mai sus arată rețeaua neuronală recurentă în formă desfășurată (în stânga) și într-o reprezentare recurentă mai compactă (în dreapta). Este important de realizat că toate celulele RNN au aceleași **greutăți partajabile**.\n",
"\n",
"Deoarece vectorii de statut $S_0,\\dots,S_n$ sunt transmiși prin rețea, RNN este capabilă să învețe dependențele secvențiale dintre cuvinte. De exemplu, atunci când cuvântul *nu* apare undeva în secvență, poate învăța să nege anumite elemente din vectorul de statut.\n",
"\n",
"În interior, fiecare celulă RNN conține două matrici de greutăți: $W_H$ și $W_I$, și un bias $b$. La fiecare pas RNN, având intrarea $X_i$ și statutul de intrare $S_i$, statutul de ieșire este calculat ca $S_{i+1} = f(W_H\\times S_i + W_I\\times X_i+b)$, unde $f$ este o funcție de activare (adesea $\\tanh$).\n",
"\n",
"> Pentru probleme precum generarea de text (pe care o vom acoperi în unitatea următoare) sau traducerea automată, dorim să obținem și o valoare de ieșire la fiecare pas RNN. În acest caz, există și o altă matrice $W_O$, iar ieșirea este calculată ca $Y_i=f(W_O\\times S_i+b_O)$.\n",
"\n",
"Să vedem cum rețelele neuronale recurente ne pot ajuta să clasificăm setul nostru de date de știri.\n",
"\n",
"> Pentru mediul sandbox, trebuie să rulăm celula următoare pentru a ne asigura că biblioteca necesară este instalată și datele sunt preluate. Dacă rulați local, puteți sări peste celula următoare.\n"
]
},
{
"cell_type": "code",
"execution_count": 1,
"metadata": {},
"outputs": [],
"source": [
"import sys\n",
"!{sys.executable} -m pip install --quiet tensorflow_datasets==4.4.0\n",
"!cd ~ && wget -q -O - https://mslearntensorflowlp.blob.core.windows.net/data/tfds-ag-news.tgz | tar xz"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {},
"outputs": [],
"source": [
"import tensorflow as tf\n",
"from tensorflow import keras\n",
"import tensorflow_datasets as tfds\n",
"import numpy as np\n",
"\n",
"# We are going to be training pretty large models. In order not to face errors, we need\n",
"# to set tensorflow option to grow GPU memory allocation when required\n",
"physical_devices = tf.config.list_physical_devices('GPU') \n",
"if len(physical_devices)>0:\n",
" tf.config.experimental.set_memory_growth(physical_devices[0], True)\n",
"\n",
"ds_train, ds_test = tfds.load('ag_news_subset').values()"
]
},
{
"cell_type": "markdown",
"metadata": {
"nteract": {
"transient": {
"deleting": false
}
}
},
"source": [
"Când antrenăm modele mari, alocarea memoriei GPU poate deveni o problemă. De asemenea, este posibil să fie nevoie să experimentăm cu dimensiuni diferite ale minibatch-urilor, astfel încât datele să încapă în memoria GPU, iar antrenarea să fie suficient de rapidă. Dacă rulați acest cod pe propria mașină GPU, puteți experimenta ajustarea dimensiunii minibatch-ului pentru a accelera antrenarea.\n",
"\n",
"> **Notă**: Se știe că anumite versiuni ale driverelor NVidia nu eliberează memoria după antrenarea modelului. Rulăm mai multe exemple în acest notebook, iar acest lucru ar putea duce la epuizarea memoriei în anumite configurații, mai ales dacă efectuați propriile experimente în cadrul aceluiași notebook. Dacă întâmpinați erori ciudate la începutul antrenării modelului, este posibil să fie necesar să reporniți kernelul notebook-ului.\n"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {
"collapsed": true,
"jupyter": {
"outputs_hidden": false,
"source_hidden": false
},
"nteract": {
"transient": {
"deleting": false
}
}
},
"outputs": [],
"source": [
"batch_size = 16\n",
"embed_size = 64"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Clasificator RNN simplu\n",
"\n",
"În cazul unui RNN simplu, fiecare unitate recurentă este o rețea liniară simplă, care primește un vector de intrare și un vector de stare, și produce un nou vector de stare. În Keras, acest lucru poate fi reprezentat prin stratul `SimpleRNN`.\n",
"\n",
"Deși putem transmite direct tokenii codificați one-hot către stratul RNN, aceasta nu este o idee bună din cauza dimensionalității lor ridicate. Prin urmare, vom folosi un strat de embedding pentru a reduce dimensionalitatea vectorilor de cuvinte, urmat de un strat RNN și, în final, un clasificator `Dense`.\n",
"\n",
"> **Notă**: În cazurile în care dimensionalitatea nu este atât de mare, de exemplu atunci când se utilizează tokenizarea la nivel de caractere, ar putea avea sens să se transmită tokenii codificați one-hot direct în celula RNN.\n"
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Model: \"sequential\"\n",
"_________________________________________________________________\n",
"Layer (type) Output Shape Param # \n",
"=================================================================\n",
"text_vectorization (TextVect (None, None) 0 \n",
"_________________________________________________________________\n",
"embedding (Embedding) (None, None, 64) 1280000 \n",
"_________________________________________________________________\n",
"simple_rnn (SimpleRNN) (None, 16) 1296 \n",
"_________________________________________________________________\n",
"dense (Dense) (None, 4) 68 \n",
"=================================================================\n",
"Total params: 1,281,364\n",
"Trainable params: 1,281,364\n",
"Non-trainable params: 0\n",
"_________________________________________________________________\n"
]
}
],
"source": [
"vocab_size = 20000\n",
"\n",
"vectorizer = keras.layers.experimental.preprocessing.TextVectorization(\n",
" max_tokens=vocab_size,\n",
" input_shape=(1,))\n",
"\n",
"model = keras.models.Sequential([\n",
" vectorizer,\n",
" keras.layers.Embedding(vocab_size, embed_size),\n",
" keras.layers.SimpleRNN(16),\n",
" keras.layers.Dense(4,activation='softmax')\n",
"])\n",
"\n",
"model.summary()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"> **Notă:** Folosim un strat de încorporare neantrenat aici pentru simplitate, dar pentru rezultate mai bune putem utiliza un strat de încorporare preantrenat folosind Word2Vec, așa cum s-a descris în unitatea anterioară. Ar fi un exercițiu bun pentru tine să adaptezi acest cod pentru a funcționa cu încorporări preantrenate.\n",
"\n",
"Acum să antrenăm RNN-ul nostru. În general, RNN-urile sunt destul de dificile de antrenat, deoarece, odată ce celulele RNN sunt desfășurate pe lungimea secvenței, numărul de straturi implicate în retropropagare devine foarte mare. Prin urmare, trebuie să selectăm o rată de învățare mai mică și să antrenăm rețeaua pe un set de date mai mare pentru a obține rezultate bune. Acest lucru poate dura destul de mult timp, așa că utilizarea unui GPU este de preferat.\n",
"\n",
"Pentru a accelera lucrurile, vom antrena modelul RNN doar pe titlurile știrilor, omisând descrierea. Poți încerca să antrenezi și cu descrierea și să vezi dacă reușești să faci modelul să se antreneze.\n"
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {
"scrolled": true
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Training vectorizer\n"
]
}
],
"source": [
"def extract_title(x):\n",
" return x['title']\n",
"\n",
"def tupelize_title(x):\n",
" return (extract_title(x),x['label'])\n",
"\n",
"print('Training vectorizer')\n",
"vectorizer.adapt(ds_train.take(2000).map(extract_title))"
]
},
{
"cell_type": "code",
"execution_count": 6,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"7500/7500 [==============================] - 82s 11ms/step - loss: 0.6629 - acc: 0.7623 - val_loss: 0.5559 - val_acc: 0.7995\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\n"
]
},
{
"data": {
"text/plain": [
"<tensorflow.python.keras.callbacks.History at 0x7f3e0030d350>"
]
},
"execution_count": 6,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"model.compile(loss='sparse_categorical_crossentropy',metrics=['acc'], optimizer='adam')\n",
"model.fit(ds_train.map(tupelize_title).batch(batch_size),validation_data=ds_test.map(tupelize_title).batch(batch_size))"
]
},
{
"cell_type": "markdown",
"metadata": {
"nteract": {
"transient": {
"deleting": false
}
}
},
"source": [
"> **Notă** că acuratețea este probabil mai scăzută aici, deoarece antrenăm doar pe titluri de știri.\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Revizuirea secvențelor variabile\n",
"\n",
"Rețineți că stratul `TextVectorization` va adăuga automat token-uri de umplere pentru a completa secvențele de lungime variabilă într-un minibatch. Se pare că acești token-uri participă și la antrenament, ceea ce poate complica convergența modelului.\n",
"\n",
"Există mai multe abordări pe care le putem adopta pentru a minimiza cantitatea de umplere. Una dintre ele este reordonarea dataset-ului în funcție de lungimea secvenței și gruparea tuturor secvențelor după dimensiune. Acest lucru poate fi realizat folosind funcția `tf.data.experimental.bucket_by_sequence_length` (vezi [documentația](https://www.tensorflow.org/api_docs/python/tf/data/experimental/bucket_by_sequence_length)).\n",
"\n",
"O altă abordare este utilizarea **maskării**. În Keras, unele straturi acceptă un input suplimentar care indică ce token-uri ar trebui luate în considerare în timpul antrenamentului. Pentru a integra maskarea în modelul nostru, putem fie să includem un strat separat `Masking` ([documentație](https://keras.io/api/layers/core_layers/masking/)), fie să specificăm parametrul `mask_zero=True` al stratului nostru `Embedding`.\n",
"\n",
"> **Note**: Acest antrenament va dura aproximativ 5 minute pentru a finaliza o epocă pe întregul dataset. Simțiți-vă liberi să întrerupeți antrenamentul în orice moment dacă vă pierdeți răbdarea. De asemenea, puteți limita cantitatea de date utilizate pentru antrenament, adăugând clauza `.take(...)` după dataset-urile `ds_train` și `ds_test`.\n"
]
},
{
"cell_type": "code",
"execution_count": 7,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"7500/7500 [==============================] - 371s 49ms/step - loss: 0.5401 - acc: 0.8079 - val_loss: 0.3780 - val_acc: 0.8822\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\n"
]
},
{
"data": {
"text/plain": [
"<tensorflow.python.keras.callbacks.History at 0x7f3dec118850>"
]
},
"execution_count": 7,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"def extract_text(x):\n",
" return x['title']+' '+x['description']\n",
"\n",
"def tupelize(x):\n",
" return (extract_text(x),x['label'])\n",
"\n",
"model = keras.models.Sequential([\n",
" vectorizer,\n",
" keras.layers.Embedding(vocab_size,embed_size,mask_zero=True),\n",
" keras.layers.SimpleRNN(16),\n",
" keras.layers.Dense(4,activation='softmax')\n",
"])\n",
"\n",
"model.compile(loss='sparse_categorical_crossentropy',metrics=['acc'], optimizer='adam')\n",
"model.fit(ds_train.map(tupelize).batch(batch_size),validation_data=ds_test.map(tupelize).batch(batch_size))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Acum că folosim mascarea, putem antrena modelul pe întregul set de date de titluri și descrieri.\n",
"\n",
"> **Notă**: Ai observat că am folosit un vectorizator antrenat pe titlurile știrilor și nu pe întregul corp al articolului? Potențial, acest lucru poate face ca unii dintre tokeni să fie ignorați, așa că ar fi mai bine să re-antrenăm vectorizatorul. Totuși, efectul ar putea fi foarte mic, așa că vom rămâne la vectorizatorul pre-antrenat anterior pentru simplitate.\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## LSTM: Memorie pe termen lung și scurt\n",
"\n",
"Una dintre principalele probleme ale RNN-urilor este **dispariția gradientului**. RNN-urile pot fi destul de lungi și pot întâmpina dificultăți în propagarea gradientelor până la primul strat al rețelei în timpul backpropagation-ului. Când se întâmplă acest lucru, rețeaua nu poate învăța relațiile dintre tokenii distanți. O modalitate de a evita această problemă este introducerea **gestionării explicite a stării** prin utilizarea **porților**. Cele mai comune două arhitecturi care introduc porți sunt **memoria pe termen lung și scurt** (LSTM) și **unitatea de retransmisie cu porți** (GRU). Vom discuta despre LSTM-uri aici.\n",
"\n",
"![Imagine care arată un exemplu de celulă de memorie pe termen lung și scurt](../../../../../lessons/5-NLP/16-RNN/images/long-short-term-memory-cell.svg)\n",
"\n",
"O rețea LSTM este organizată într-un mod similar cu un RNN, dar există două stări care sunt transmise de la un strat la altul: starea propriu-zisă $c$ și vectorul ascuns $h$. La fiecare unitate, vectorul ascuns $h_{t-1}$ este combinat cu intrarea $x_t$, iar împreună controlează ce se întâmplă cu starea $c_t$ și ieșirea $h_{t}$ prin intermediul **porților**. Fiecare poartă are o activare sigmoidă (ieșire în intervalul $[0,1]$), care poate fi considerată ca o mască bit cu bit atunci când este înmulțită cu vectorul de stare. LSTM-urile au următoarele porți (de la stânga la dreapta în imaginea de mai sus):\n",
"* **Poarta de uitare**, care determină ce componente ale vectorului $c_{t-1}$ trebuie să uităm și care să fie transmise mai departe.\n",
"* **Poarta de intrare**, care determină câtă informație din vectorul de intrare și vectorul ascuns anterior ar trebui să fie încorporată în vectorul de stare.\n",
"* **Poarta de ieșire**, care ia noul vector de stare și decide care dintre componentele sale vor fi utilizate pentru a produce noul vector ascuns $h_t$.\n",
"\n",
"Componentele stării $c$ pot fi considerate ca niște steaguri care pot fi activate sau dezactivate. De exemplu, când întâlnim numele *Alice* într-o secvență, presupunem că se referă la o femeie și ridicăm steagul în stare care indică faptul că avem un substantiv feminin în propoziție. Când întâlnim ulterior cuvintele *și Tom*, vom ridica steagul care indică faptul că avem un substantiv la plural. Astfel, prin manipularea stării, putem urmări proprietățile gramaticale ale propoziției.\n",
"\n",
"> **Note**: Iată o resursă excelentă pentru a înțelege structura internă a LSTM-urilor: [Understanding LSTM Networks](https://colah.github.io/posts/2015-08-Understanding-LSTMs/) de Christopher Olah.\n",
"\n",
"Deși structura internă a unei celule LSTM poate părea complexă, Keras ascunde această implementare în interiorul stratului `LSTM`, astfel încât singurul lucru pe care trebuie să-l facem în exemplul de mai sus este să înlocuim stratul recurent:\n"
]
},
{
"cell_type": "code",
"execution_count": 8,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"15000/15000 [==============================] - 188s 13ms/step - loss: 0.5692 - acc: 0.7916 - val_loss: 0.3441 - val_acc: 0.8870\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\n"
]
},
{
"data": {
"text/plain": [
"<tensorflow.python.keras.callbacks.History at 0x7f3d6af5c350>"
]
},
"execution_count": 8,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"model = keras.models.Sequential([\n",
" vectorizer,\n",
" keras.layers.Embedding(vocab_size, embed_size),\n",
" keras.layers.LSTM(8),\n",
" keras.layers.Dense(4,activation='softmax')\n",
"])\n",
"\n",
"model.compile(loss='sparse_categorical_crossentropy',metrics=['acc'], optimizer='adam')\n",
"model.fit(ds_train.map(tupelize).batch(8),validation_data=ds_test.map(tupelize).batch(8))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": []
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## RNN-uri bidirecționale și multilayer\n",
"\n",
"În exemplele noastre de până acum, rețelele recurente operează de la începutul unei secvențe până la sfârșit. Acest lucru ni se pare natural, deoarece urmează aceeași direcție în care citim sau ascultăm vorbirea. Totuși, pentru scenarii care necesită acces aleatoriu la secvența de intrare, are mai mult sens să rulăm calculul recurent în ambele direcții. RNN-urile care permit calcule în ambele direcții se numesc **RNN-uri bidirecționale**, și pot fi create prin învelirea stratului recurent cu un strat special `Bidirectional`.\n",
"\n",
"> **Note**: Stratul `Bidirectional` face două copii ale stratului din interiorul său și setează proprietatea `go_backwards` a uneia dintre aceste copii la `True`, făcând-o să meargă în direcția opusă de-a lungul secvenței.\n",
"\n",
"Rețelele recurente, unidirecționale sau bidirecționale, captează modele dintr-o secvență și le stochează în vectori de stare sau le returnează ca ieșire. La fel ca în cazul rețelelor convoluționale, putem construi un alt strat recurent care urmează primului pentru a capta modele de nivel superior, construite din modelele de nivel inferior extrase de primul strat. Acest lucru ne conduce la noțiunea de **RNN multilayer**, care constă din două sau mai multe rețele recurente, unde ieșirea stratului anterior este transmisă stratului următor ca intrare.\n",
"\n",
"![Imagine care arată un RNN multilayer cu memorie pe termen lung și scurt](../../../../../translated_images/multi-layer-lstm.dd975e29bb2a59fe58b429db833932d734c81f211cad2783797a9608984acb8c.ro.jpg)\n",
"\n",
"*Imagine din [această postare minunată](https://towardsdatascience.com/from-a-lstm-cell-to-a-multilayer-lstm-network-with-pytorch-2899eb5696f3) de Fernando López.*\n",
"\n",
"Keras face construirea acestor rețele o sarcină ușoară, deoarece trebuie doar să adăugați mai multe straturi recurente la model. Pentru toate straturile, cu excepția ultimului, trebuie să specificăm parametrul `return_sequences=True`, deoarece avem nevoie ca stratul să returneze toate stările intermediare, nu doar starea finală a calculului recurent.\n",
"\n",
"Să construim un LSTM bidirecțional cu două straturi pentru problema noastră de clasificare.\n",
"\n",
"> **Note** acest cod durează din nou destul de mult timp pentru a se finaliza, dar ne oferă cea mai mare acuratețe pe care am văzut-o până acum. Deci, poate merită să așteptăm și să vedem rezultatul.\n"
]
},
{
"cell_type": "code",
"execution_count": 9,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"5044/7500 [===================>..........] - ETA: 2:33 - loss: 0.3709 - acc: 0.8706\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\r5045/7500 [===================>..........] - ETA: 2:33 - loss: 0.3709 - acc: 0.8706"
]
}
],
"source": [
"model = keras.models.Sequential([\n",
" vectorizer,\n",
" keras.layers.Embedding(vocab_size, 128, mask_zero=True),\n",
" keras.layers.Bidirectional(keras.layers.LSTM(64,return_sequences=True)),\n",
" keras.layers.Bidirectional(keras.layers.LSTM(64)), \n",
" keras.layers.Dense(4,activation='softmax')\n",
"])\n",
"\n",
"model.compile(loss='sparse_categorical_crossentropy',metrics=['acc'], optimizer='adam')\n",
"model.fit(ds_train.map(tupelize).batch(batch_size),\n",
" validation_data=ds_test.map(tupelize).batch(batch_size))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## RNN-uri pentru alte sarcini\n",
"\n",
"Până acum, ne-am concentrat pe utilizarea RNN-urilor pentru clasificarea secvențelor de text. Dar ele pot gestiona multe alte sarcini, cum ar fi generarea de text și traducerea automată — vom analiza aceste sarcini în unitatea următoare.\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"\n---\n\n**Declinare de responsabilitate**: \nAcest document a fost tradus folosind serviciul de traducere AI [Co-op Translator](https://github.com/Azure/co-op-translator). Deși ne străduim să asigurăm acuratețea, vă rugăm să rețineți că traducerile automate pot conține erori sau inexactități. Documentul original în limba sa natală ar trebui considerat sursa autoritară. Pentru informații critice, se recomandă traducerea profesională realizată de un specialist uman. Nu ne asumăm responsabilitatea pentru eventualele neînțelegeri sau interpretări greșite care pot apărea din utilizarea acestei traduceri.\n"
]
}
],
"metadata": {
"kernel_info": {
"name": "conda-env-py37_tensorflow-py"
},
"kernelspec": {
"display_name": "py37_tensorflow",
"language": "python",
"name": "conda-env-py37_tensorflow-py"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.7.9"
},
"nteract": {
"version": "nteract-front-end@1.0.0"
},
"coopTranslator": {
"original_hash": "81351e61f619b432ff51010a4f993194",
"translation_date": "2025-08-30T00:55:46+00:00",
"source_file": "lessons/5-NLP/16-RNN/RNNTF.ipynb",
"language_code": "ro"
}
},
"nbformat": 4,
"nbformat_minor": 4
}

View File

@ -0,0 +1,414 @@
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Rețele generative\n",
"\n",
"Rețelele Neuronale Recurente (RNN) și variantele lor cu celule cu porți, cum ar fi Celulele cu Memorie pe Termen Lung (LSTM) și Unitățile Recurente cu Porți (GRU), au oferit un mecanism pentru modelarea limbajului, adică pot învăța ordonarea cuvintelor și pot oferi predicții pentru următorul cuvânt dintr-o secvență. Acest lucru ne permite să folosim RNN-urile pentru **sarcini generative**, cum ar fi generarea obișnuită de text, traducerea automată și chiar generarea de descrieri pentru imagini.\n",
"\n",
"În arhitectura RNN discutată în unitatea anterioară, fiecare unitate RNN producea următoarea stare ascunsă ca ieșire. Totuși, putem adăuga și o altă ieșire fiecărei unități recurente, ceea ce ne-ar permite să generăm o **secvență** (care este egală ca lungime cu secvența originală). Mai mult, putem folosi unități RNN care nu acceptă o intrare la fiecare pas, ci doar primesc un vector de stare inițială și apoi produc o secvență de ieșiri.\n",
"\n",
"În acest notebook, ne vom concentra pe modele generative simple care ne ajută să generăm text. Pentru simplitate, să construim o **rețea la nivel de caractere**, care generează text literă cu literă. În timpul antrenării, trebuie să luăm un corpus de text și să-l împărțim în secvențe de litere.\n"
]
},
{
"cell_type": "code",
"execution_count": 1,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Loading dataset...\n",
"Building vocab...\n"
]
}
],
"source": [
"import torch\n",
"import torchtext\n",
"import numpy as np\n",
"from torchnlp import *\n",
"train_dataset,test_dataset,classes,vocab = load_dataset()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Construirea vocabularului de caractere\n",
"\n",
"Pentru a construi o rețea generativă la nivel de caractere, trebuie să împărțim textul în caractere individuale, în loc de cuvinte. Acest lucru poate fi realizat prin definirea unui tokenizator diferit:\n"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Vocabulary size = 82\n",
"Encoding of 'a' is 1\n",
"Character with code 13 is c\n"
]
}
],
"source": [
"def char_tokenizer(words):\n",
" return list(words) #[word for word in words]\n",
"\n",
"counter = collections.Counter()\n",
"for (label, line) in train_dataset:\n",
" counter.update(char_tokenizer(line))\n",
"vocab = torchtext.vocab.vocab(counter)\n",
"\n",
"vocab_size = len(vocab)\n",
"print(f\"Vocabulary size = {vocab_size}\")\n",
"print(f\"Encoding of 'a' is {vocab.get_stoi()['a']}\")\n",
"print(f\"Character with code 13 is {vocab.get_itos()[13]}\")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Să vedem exemplul de cum putem codifica textul din setul nostru de date:\n"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"tensor([ 0, 1, 2, 2, 3, 4, 5, 6, 3, 7, 8, 1, 9, 10, 3, 11, 2, 1,\n",
" 12, 3, 7, 1, 13, 14, 3, 15, 16, 5, 17, 3, 5, 18, 8, 3, 7, 2,\n",
" 1, 13, 14, 3, 19, 20, 8, 21, 5, 8, 9, 10, 22, 3, 20, 8, 21, 5,\n",
" 8, 9, 10, 3, 23, 3, 4, 18, 17, 9, 5, 23, 10, 8, 2, 2, 8, 9,\n",
" 10, 24, 3, 0, 1, 2, 2, 3, 4, 5, 9, 8, 8, 5, 25, 10, 3, 26,\n",
" 12, 27, 16, 26, 2, 27, 16, 28, 29, 30, 1, 16, 26, 3, 17, 31, 3, 21,\n",
" 2, 5, 9, 1, 23, 13, 32, 16, 27, 13, 10, 24, 3, 1, 9, 8, 3, 10,\n",
" 8, 8, 27, 16, 28, 3, 28, 9, 8, 8, 16, 3, 1, 28, 1, 27, 16, 6])"
]
},
"execution_count": 3,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"def enc(x):\n",
" return torch.LongTensor(encode(x,voc=vocab,tokenizer=char_tokenizer))\n",
"\n",
"enc(train_dataset[0][1])"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Antrenarea unui RNN generativ\n",
"\n",
"Modul în care vom antrena RNN pentru a genera text este următorul. La fiecare pas, vom lua o secvență de caractere de lungime `nchars` și vom cere rețelei să genereze următorul caracter de ieșire pentru fiecare caracter de intrare:\n",
"\n",
"![Imagine care arată un exemplu de generare RNN a cuvântului 'HELLO'.](../../../../../translated_images/rnn-generate.56c54afb52f9781d63a7c16ea9c1b86cb70e6e1eae6a742b56b7b37468576b17.ro.png)\n",
"\n",
"În funcție de scenariul concret, este posibil să dorim să includem și câteva caractere speciale, cum ar fi *sfârșit-de-secvență* `<eos>`. În cazul nostru, dorim doar să antrenăm rețeaua pentru generarea continuă de text, așa că vom fixa dimensiunea fiecărei secvențe să fie egală cu `nchars` tokeni. Prin urmare, fiecare exemplu de antrenament va consta din `nchars` intrări și `nchars` ieșiri (care sunt secvența de intrare deplasată cu un simbol spre stânga). Un minibatch va consta din mai multe astfel de secvențe.\n",
"\n",
"Modul în care vom genera minibatch-uri este să luăm fiecare text de știri de lungime `l` și să generăm toate combinațiile posibile de intrare-ieșire din acesta (vor exista `l-nchars` astfel de combinații). Acestea vor forma un minibatch, iar dimensiunea minibatch-urilor va fi diferită la fiecare pas de antrenament.\n"
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"(tensor([[ 0, 1, 2, ..., 28, 29, 30],\n",
" [ 1, 2, 2, ..., 29, 30, 1],\n",
" [ 2, 2, 3, ..., 30, 1, 16],\n",
" ...,\n",
" [20, 8, 21, ..., 1, 28, 1],\n",
" [ 8, 21, 5, ..., 28, 1, 27],\n",
" [21, 5, 8, ..., 1, 27, 16]]),\n",
" tensor([[ 1, 2, 2, ..., 29, 30, 1],\n",
" [ 2, 2, 3, ..., 30, 1, 16],\n",
" [ 2, 3, 4, ..., 1, 16, 26],\n",
" ...,\n",
" [ 8, 21, 5, ..., 28, 1, 27],\n",
" [21, 5, 8, ..., 1, 27, 16],\n",
" [ 5, 8, 9, ..., 27, 16, 6]]))"
]
},
"execution_count": 4,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"nchars = 100\n",
"\n",
"def get_batch(s,nchars=nchars):\n",
" ins = torch.zeros(len(s)-nchars,nchars,dtype=torch.long,device=device)\n",
" outs = torch.zeros(len(s)-nchars,nchars,dtype=torch.long,device=device)\n",
" for i in range(len(s)-nchars):\n",
" ins[i] = enc(s[i:i+nchars])\n",
" outs[i] = enc(s[i+1:i+nchars+1])\n",
" return ins,outs\n",
"\n",
"get_batch(train_dataset[0][1])"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Acum să definim rețeaua generatorului. Aceasta poate fi bazată pe orice celulă recurentă despre care am discutat în unitatea anterioară (simplă, LSTM sau GRU). În exemplul nostru vom folosi LSTM.\n",
"\n",
"Deoarece rețeaua primește caractere ca intrare, iar dimensiunea vocabularului este destul de mică, nu avem nevoie de un strat de încorporare (embedding), intrarea codificată one-hot poate fi trimisă direct către celula LSTM. Totuși, deoarece transmitem numerele caracterelor ca intrare, trebuie să le codificăm one-hot înainte de a le trimite către LSTM. Acest lucru se face apelând funcția `one_hot` în timpul trecerii `forward`. Codificatorul de ieșire va fi un strat liniar care va converti starea ascunsă într-o ieșire codificată one-hot.\n"
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {},
"outputs": [],
"source": [
"class LSTMGenerator(torch.nn.Module):\n",
" def __init__(self, vocab_size, hidden_dim):\n",
" super().__init__()\n",
" self.rnn = torch.nn.LSTM(vocab_size,hidden_dim,batch_first=True)\n",
" self.fc = torch.nn.Linear(hidden_dim, vocab_size)\n",
"\n",
" def forward(self, x, s=None):\n",
" x = torch.nn.functional.one_hot(x,vocab_size).to(torch.float32)\n",
" x,s = self.rnn(x,s)\n",
" return self.fc(x),s"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"În timpul antrenamentului, dorim să putem eșantiona text generat. Pentru a face acest lucru, vom defini funcția `generate`, care va produce un șir de caractere de lungime `size`, începând de la șirul inițial `start`.\n",
"\n",
"Modul în care funcționează este următorul. Mai întâi, vom trece întregul șir `start` prin rețea și vom obține starea de ieșire `s` și următorul caracter prezis `out`. Deoarece `out` este codificat one-hot, folosim `argmax` pentru a obține indexul caracterului `nc` din vocabular și utilizăm `itos` pentru a determina caracterul real, pe care îl adăugăm la lista rezultată de caractere `chars`. Acest proces de generare a unui caracter este repetat de `size` ori pentru a genera numărul necesar de caractere.\n"
]
},
{
"cell_type": "code",
"execution_count": 8,
"metadata": {},
"outputs": [],
"source": [
"def generate(net,size=100,start='today '):\n",
" chars = list(start)\n",
" out, s = net(enc(chars).view(1,-1).to(device))\n",
" for i in range(size):\n",
" nc = torch.argmax(out[0][-1])\n",
" chars.append(vocab.get_itos()[nc])\n",
" out, s = net(nc.view(1,-1),s)\n",
" return ''.join(chars)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Acum să trecem la antrenament! Bucla de antrenament este aproape aceeași ca în toate exemplele noastre anterioare, dar în loc să afișăm acuratețea, generăm și afișăm text eșantionat la fiecare 1000 de epoci.\n",
"\n",
"O atenție specială trebuie acordată modului în care calculăm pierderea. Trebuie să calculăm pierderea având ca intrare ieșirea codificată one-hot `out` și textul așteptat `text_out`, care este lista indicilor caracterelor. Din fericire, funcția `cross_entropy` așteaptă ca prim argument ieșirea ne-normalizată a rețelei și ca al doilea argument numărul clasei, ceea ce este exact ceea ce avem. De asemenea, efectuează automat media pe dimensiunea minibatch-ului.\n",
"\n",
"De asemenea, limităm antrenamentul la un număr de mostre definit de `samples_to_train`, pentru a nu aștepta prea mult. Vă încurajăm să experimentați și să încercați antrenamente mai lungi, posibil pentru mai multe epoci (în acest caz, ar trebui să creați o altă buclă în jurul acestui cod).\n"
]
},
{
"cell_type": "code",
"execution_count": 9,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Current loss = 4.398899078369141\n",
"today sr sr sr sr sr sr sr sr sr sr sr sr sr sr sr sr sr sr sr sr sr sr sr sr sr sr sr sr sr sr sr sr sr s\n",
"Current loss = 2.161320447921753\n",
"today and to the tor to to the tor to to the tor to to the tor to to the tor to to the tor to to the tor t\n",
"Current loss = 1.6722588539123535\n",
"today and the court to the could to the could to the could to the could to the could to the could to the c\n",
"Current loss = 2.423795223236084\n",
"today and a second to the conternation of the conternation of the conternation of the conternation of the \n",
"Current loss = 1.702607274055481\n",
"today and the company to the company to the company to the company to the company to the company to the co\n",
"Current loss = 1.692358136177063\n",
"today and the company to the company to the company to the company to the company to the company to the co\n",
"Current loss = 1.9722288846969604\n",
"today and the control the control the control the control the control the control the control the control \n",
"Current loss = 1.8705692291259766\n",
"today and the second to the second to the second to the second to the second to the second to the second t\n",
"Current loss = 1.7626899480819702\n",
"today and a security and a security and a security and a security and a security and a security and a secu\n",
"Current loss = 1.5574463605880737\n",
"today and the company and the company and the company and the company and the company and the company and \n",
"Current loss = 1.5620026588439941\n",
"today and the be that the be the be that the be the be that the be the be that the be the be that the be t\n"
]
}
],
"source": [
"net = LSTMGenerator(vocab_size,64).to(device)\n",
"\n",
"samples_to_train = 10000\n",
"optimizer = torch.optim.Adam(net.parameters(),0.01)\n",
"loss_fn = torch.nn.CrossEntropyLoss()\n",
"net.train()\n",
"for i,x in enumerate(train_dataset):\n",
" # x[0] is class label, x[1] is text\n",
" if len(x[1])-nchars<10:\n",
" continue\n",
" samples_to_train-=1\n",
" if not samples_to_train: break\n",
" text_in, text_out = get_batch(x[1])\n",
" optimizer.zero_grad()\n",
" out,s = net(text_in)\n",
" loss = torch.nn.functional.cross_entropy(out.view(-1,vocab_size),text_out.flatten()) #cross_entropy(out,labels)\n",
" loss.backward()\n",
" optimizer.step()\n",
" if i%1000==0:\n",
" print(f\"Current loss = {loss.item()}\")\n",
" print(generate(net))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Acest exemplu generează deja un text destul de bun, dar poate fi îmbunătățit în mai multe moduri:\n",
"\n",
"* **Generarea mai bună a minibatch-urilor**. Modul în care am pregătit datele pentru antrenament a fost să generăm un minibatch dintr-un singur eșantion. Acest lucru nu este ideal, deoarece minibatch-urile au dimensiuni diferite, iar unele dintre ele nici măcar nu pot fi generate, deoarece textul este mai mic decât `nchars`. De asemenea, minibatch-urile mici nu utilizează suficient GPU-ul. Ar fi mai înțelept să luăm un bloc mare de text din toate eșantioanele, să generăm toate perechile input-output, să le amestecăm și să generăm minibatch-uri de dimensiuni egale.\n",
"\n",
"* **LSTM multilayer**. Are sens să încercăm 2 sau 3 straturi de celule LSTM. Așa cum am menționat în unitatea anterioară, fiecare strat de LSTM extrage anumite tipare din text, iar în cazul unui generator la nivel de caractere, ne putem aștepta ca nivelul inferior al LSTM să fie responsabil pentru extragerea silabelor, iar nivelurile superioare - pentru cuvinte și combinații de cuvinte. Acest lucru poate fi implementat simplu prin transmiterea unui parametru pentru numărul de straturi către constructorul LSTM.\n",
"\n",
"* De asemenea, ai putea să experimentezi cu **unități GRU** și să vezi care dintre ele oferă performanțe mai bune, precum și cu **dimensiuni diferite ale straturilor ascunse**. Un strat ascuns prea mare poate duce la overfitting (de exemplu, rețeaua va învăța textul exact), iar o dimensiune mai mică s-ar putea să nu producă un rezultat bun.\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Generarea textului moale și temperatura\n",
"\n",
"În definiția anterioară a funcției `generate`, alegeam întotdeauna caracterul cu cea mai mare probabilitate ca următorul caracter în textul generat. Acest lucru ducea adesea la faptul că textul \"cicla\" între aceleași secvențe de caractere din nou și din nou, ca în acest exemplu:\n",
"```\n",
"today of the second the company and a second the company ...\n",
"```\n",
"\n",
"Totuși, dacă ne uităm la distribuția probabilităților pentru următorul caracter, este posibil ca diferența dintre câteva dintre cele mai mari probabilități să nu fie semnificativă, de exemplu, un caracter poate avea probabilitatea 0.2, iar altul 0.19, etc. De exemplu, când căutăm următorul caracter în secvența '*play*', următorul caracter poate fi la fel de bine un spațiu sau **e** (ca în cuvântul *player*).\n",
"\n",
"Acest lucru ne conduce la concluzia că nu este întotdeauna \"corect\" să selectăm caracterul cu probabilitatea cea mai mare, deoarece alegerea celui de-al doilea cel mai probabil caracter poate duce, de asemenea, la un text semnificativ. Este mai înțelept să **eșantionăm** caracterele din distribuția probabilităților oferită de rezultatul rețelei.\n",
"\n",
"Această eșantionare poate fi realizată folosind funcția `multinomial`, care implementează așa-numita **distribuție multinomială**. O funcție care implementează această generare de text **moale** este definită mai jos:\n"
]
},
{
"cell_type": "code",
"execution_count": 10,
"metadata": {
"scrolled": true
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"--- Temperature = 0.3\n",
"Today and a company and complete an all the land the restrational the as a security and has provers the pay to and a report and the computer in the stand has filities and working the law the stations for a company and with the company and the final the first company and refight of the state and and workin\n",
"\n",
"--- Temperature = 0.8\n",
"Today he oniis its first to Aus bomblaties the marmation a to manan boogot that pirate assaid a relaid their that goverfin the the Cappets Ecrotional Assonia Cition targets it annight the w scyments Blamity #39;s TVeer Diercheg Reserals fran envyuil that of ster said access what succers of Dour-provelith\n",
"\n",
"--- Temperature = 1.0\n",
"Today holy they a 11 will meda a toket subsuaties, engins for Chanos, they's has stainger past to opening orital his thempting new Nattona was al innerforder advan-than #36;s night year his religuled talitatian what the but with Wednesday to Justment will wemen of Mark CCC Camp as Timed Nae wome a leaders\n",
"\n",
"--- Temperature = 1.3\n",
"Today gpone 2.5 fech atcusion poor cocles toparsdorM.cht Line Pamage put 43 his calt lowed to the book, that has authh-the silia rruch ailing to'ory andhes beutirsimi- Aefffive heading offil an auf eacklets is charged evis, Gunymy oy) Mony has it after-sloythyor loveId out filme, the Natabl -Najuntaxiggs \n",
"\n",
"--- Temperature = 1.8\n",
"Today plary, P.slan chly\\401 mardregationly #39;t 8.1Mide) closes ,filtcon alfly playin roven!\\grea.-QFBEP: Iss onfarchQ/itilia CCf Zivesigntwasta orce.-Peul-aw.uicrin of fuglinfsut aftaningwo, MIEX awayew Aice Woiduar Corvagiugge oppo esig ThusBratourid canthly-RyI.co lagitems\\eexciaishes.conBabntusmor I\n",
"\n"
]
}
],
"source": [
"def generate_soft(net,size=100,start='today ',temperature=1.0):\n",
" chars = list(start)\n",
" out, s = net(enc(chars).view(1,-1).to(device))\n",
" for i in range(size):\n",
" #nc = torch.argmax(out[0][-1])\n",
" out_dist = out[0][-1].div(temperature).exp()\n",
" nc = torch.multinomial(out_dist,1)[0]\n",
" chars.append(vocab.get_itos()[nc])\n",
" out, s = net(nc.view(1,-1),s)\n",
" return ''.join(chars)\n",
" \n",
"for i in [0.3,0.8,1.0,1.3,1.8]:\n",
" print(f\"--- Temperature = {i}\\n{generate_soft(net,size=300,start='Today ',temperature=i)}\\n\")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Am introdus încă un parametru numit **temperatură**, care este utilizat pentru a indica cât de strict ar trebui să ne bazăm pe cea mai mare probabilitate. Dacă temperatura este 1.0, efectuăm o eșantionare multinomială echitabilă, iar când temperatura crește spre infinit - toate probabilitățile devin egale și selectăm aleator următorul caracter. În exemplul de mai jos putem observa că textul devine lipsit de sens atunci când creștem temperatura prea mult și seamănă cu un text \"ciclic\" generat rigid atunci când se apropie de 0.\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"\n---\n\n**Declinare de responsabilitate**: \nAcest document a fost tradus folosind serviciul de traducere AI [Co-op Translator](https://github.com/Azure/co-op-translator). Deși ne străduim să asigurăm acuratețea, vă rugăm să fiți conștienți că traducerile automate pot conține erori sau inexactități. Documentul original în limba sa natală ar trebui considerat sursa autoritară. Pentru informații critice, se recomandă traducerea profesională realizată de un specialist uman. Nu ne asumăm responsabilitatea pentru eventualele neînțelegeri sau interpretări greșite care pot apărea din utilizarea acestei traduceri.\n"
]
}
],
"metadata": {
"interpreter": {
"hash": "16af2a8bbb083ea23e5e41c7f5787656b2ce26968575d8763f2c4b17f9cd711f"
},
"kernelspec": {
"display_name": "Python 3.8.12 ('py38')",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.8.12"
},
"coopTranslator": {
"original_hash": "7673cd150d96c74c6d6011460094efb4",
"translation_date": "2025-08-30T00:37:39+00:00",
"source_file": "lessons/5-NLP/17-GenerativeNetworks/GenerativePyTorch.ipynb",
"language_code": "ro"
}
},
"nbformat": 4,
"nbformat_minor": 4
}

View File

@ -0,0 +1,495 @@
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Rețele generative\n",
"\n",
"Rețelele Neuronale Recurente (RNN) și variantele lor cu celule cu porți, cum ar fi Celulele cu Memorie pe Termen Lung (LSTM) și Unitățile Recurente cu Porți (GRU), au oferit un mecanism pentru modelarea limbajului, adică pot învăța ordonarea cuvintelor și pot oferi predicții pentru următorul cuvânt dintr-o secvență. Acest lucru ne permite să folosim RNN-urile pentru **sarcini generative**, cum ar fi generarea obișnuită de text, traducerea automată și chiar generarea de descrieri pentru imagini.\n",
"\n",
"În arhitectura RNN discutată în unitatea anterioară, fiecare unitate RNN producea următoarea stare ascunsă ca ieșire. Totuși, putem adăuga și o altă ieșire fiecărei unități recurente, ceea ce ne-ar permite să generăm o **secvență** (care este egală ca lungime cu secvența originală). Mai mult, putem folosi unități RNN care nu acceptă o intrare la fiecare pas, ci doar primesc un vector de stare inițială și apoi produc o secvență de ieșiri.\n",
"\n",
"În acest notebook, ne vom concentra pe modele generative simple care ne ajută să generăm text. Pentru simplitate, să construim o **rețea la nivel de caractere**, care generează text literă cu literă. În timpul antrenării, trebuie să luăm un corpus de text și să-l împărțim în secvențe de litere.\n"
]
},
{
"cell_type": "code",
"execution_count": 1,
"metadata": {},
"outputs": [],
"source": [
"import tensorflow as tf\n",
"from tensorflow import keras\n",
"import tensorflow_datasets as tfds\n",
"import numpy as np\n",
"\n",
"ds_train, ds_test = tfds.load('ag_news_subset').values()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Construirea vocabularului de caractere\n",
"\n",
"Pentru a construi o rețea generativă la nivel de caractere, trebuie să împărțim textul în caractere individuale, în loc de cuvinte. Stratul `TextVectorization` pe care l-am folosit anterior nu poate face acest lucru, așa că avem două opțiuni:\n",
"\n",
"* Încărcarea manuală a textului și realizarea tokenizării „manual”, așa cum se arată în [acest exemplu oficial Keras](https://keras.io/examples/generative/lstm_character_level_text_generation/)\n",
"* Utilizarea clasei `Tokenizer` pentru tokenizarea la nivel de caractere.\n",
"\n",
"Vom alege a doua opțiune. `Tokenizer` poate fi folosit și pentru tokenizarea în cuvinte, astfel încât să fie ușor de trecut de la tokenizarea la nivel de caractere la cea la nivel de cuvinte.\n",
"\n",
"Pentru a realiza tokenizarea la nivel de caractere, trebuie să transmitem parametrul `char_level=True`:\n"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {},
"outputs": [],
"source": [
"def extract_text(x):\n",
" return x['title']+' '+x['description']\n",
"\n",
"def tupelize(x):\n",
" return (extract_text(x),x['label'])\n",
"\n",
"tokenizer = keras.preprocessing.text.Tokenizer(char_level=True,lower=False)\n",
"tokenizer.fit_on_texts([x['title'].numpy().decode('utf-8') for x in ds_train])"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Vrem să folosim și un token special pentru a indica **sfârșitul secvenței**, pe care îl vom numi `<eos>`. Să-l adăugăm manual la vocabular:\n"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {},
"outputs": [],
"source": [
"eos_token = len(tokenizer.word_index)+1\n",
"tokenizer.word_index['<eos>'] = eos_token\n",
"\n",
"vocab_size = eos_token + 1"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": []
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"[[48, 2, 10, 10, 5, 44, 1, 25, 5, 8, 10, 13, 78]]"
]
},
"execution_count": 4,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"tokenizer.texts_to_sequences(['Hello, world!'])"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Antrenarea unui RNN generativ pentru a genera titluri\n",
"\n",
"Modul în care vom antrena RNN pentru a genera titluri de știri este următorul. La fiecare pas, vom lua un titlu, care va fi introdus într-un RNN, iar pentru fiecare caracter de intrare vom cere rețelei să genereze următorul caracter de ieșire:\n",
"\n",
"![Imagine care arată un exemplu de generare RNN a cuvântului 'HELLO'.](../../../../../translated_images/rnn-generate.56c54afb52f9781d63a7c16ea9c1b86cb70e6e1eae6a742b56b7b37468576b17.ro.png)\n",
"\n",
"Pentru ultimul caracter al secvenței noastre, vom cere rețelei să genereze token-ul `<eos>`.\n",
"\n",
"Principala diferență între RNN-ul generativ pe care îl folosim aici este că vom lua o ieșire de la fiecare pas al RNN-ului, și nu doar de la celula finală. Acest lucru poate fi realizat prin specificarea parametrului `return_sequences` pentru celula RNN.\n",
"\n",
"Astfel, în timpul antrenamentului, o intrare în rețea ar fi o secvență de caractere codificate de o anumită lungime, iar o ieșire ar fi o secvență de aceeași lungime, dar deplasată cu un element și terminată cu `<eos>`. Un minibatch va consta din mai multe astfel de secvențe, iar noi va trebui să folosim **padding** pentru a alinia toate secvențele.\n",
"\n",
"Să creăm funcții care vor transforma setul de date pentru noi. Deoarece dorim să completăm secvențele la nivel de minibatch, mai întâi vom grupa setul de date apelând `.batch()`, iar apoi îl vom `map`-a pentru a face transformarea. Așadar, funcția de transformare va lua un întreg minibatch ca parametru:\n"
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {},
"outputs": [],
"source": [
"def title_batch(x):\n",
" x = [t.numpy().decode('utf-8') for t in x]\n",
" z = tokenizer.texts_to_sequences(x)\n",
" z = tf.keras.preprocessing.sequence.pad_sequences(z)\n",
" return tf.one_hot(z,vocab_size), tf.one_hot(tf.concat([z[:,1:],tf.constant(eos_token,shape=(len(z),1))],axis=1),vocab_size)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Câteva lucruri importante pe care le facem aici:\n",
"* Mai întâi extragem textul efectiv din tensorul de tip string\n",
"* `text_to_sequences` convertește lista de șiruri de caractere într-o listă de tensori de tip întreg\n",
"* `pad_sequences` completează acești tensori la lungimea lor maximă\n",
"* În final, codificăm one-hot toți caracterii, realizăm și deplasarea, precum și adăugarea `<eos>`. Vom vedea în curând de ce avem nevoie de caractere codificate one-hot.\n",
"\n",
"Totuși, această funcție este **Pythonică**, adică nu poate fi tradusă automat într-un grafic computațional Tensorflow. Vom primi erori dacă încercăm să folosim această funcție direct în funcția `Dataset.map`. Trebuie să închidem acest apel Pythonic utilizând wrapper-ul `py_function`:\n"
]
},
{
"cell_type": "code",
"execution_count": 6,
"metadata": {},
"outputs": [],
"source": [
"def title_batch_fn(x):\n",
" x = x['title']\n",
" a,b = tf.py_function(title_batch,inp=[x],Tout=(tf.float32,tf.float32))\n",
" return a,b"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"> **Note**: Diferențierea între funcțiile de transformare Pythonic și cele Tensorflow poate părea puțin prea complicată, și poate te întrebi de ce nu transformăm setul de date folosind funcții standard Python înainte de a-l transmite către `fit`. Deși acest lucru se poate face cu siguranță, utilizarea `Dataset.map` are un mare avantaj, deoarece pipeline-ul de transformare a datelor este executat folosind graful computațional Tensorflow, care profită de calculele GPU și minimizează necesitatea de a transfera date între CPU/GPU.\n",
"\n",
"Acum putem construi rețeaua noastră generator și începe antrenarea. Aceasta poate fi bazată pe orice celulă recurentă pe care am discutat-o în unitatea anterioară (simplă, LSTM sau GRU). În exemplul nostru vom folosi LSTM.\n",
"\n",
"Deoarece rețeaua primește caractere ca input, iar dimensiunea vocabularului este destul de mică, nu avem nevoie de un strat de embedding; input-ul codificat one-hot poate fi transmis direct în celula LSTM. Stratul de ieșire va fi un clasificator `Dense` care va converti ieșirea LSTM în numere de token-uri codificate one-hot.\n",
"\n",
"În plus, deoarece lucrăm cu secvențe de lungime variabilă, putem folosi stratul `Masking` pentru a crea o mască care va ignora partea umplută a șirului. Acest lucru nu este strict necesar, deoarece nu suntem foarte interesați de tot ceea ce depășește token-ul `<eos>`, dar îl vom folosi pentru a câștiga ceva experiență cu acest tip de strat. `input_shape` va fi `(None, vocab_size)`, unde `None` indică secvența de lungime variabilă, iar forma de ieșire este `(None, vocab_size)` de asemenea, așa cum poți vedea din `summary`:\n"
]
},
{
"cell_type": "code",
"execution_count": 7,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Model: \"sequential\"\n",
"_________________________________________________________________\n",
"Layer (type) Output Shape Param # \n",
"=================================================================\n",
"masking (Masking) (None, None, 84) 0 \n",
"_________________________________________________________________\n",
"lstm (LSTM) (None, None, 128) 109056 \n",
"_________________________________________________________________\n",
"dense (Dense) (None, None, 84) 10836 \n",
"=================================================================\n",
"Total params: 119,892\n",
"Trainable params: 119,892\n",
"Non-trainable params: 0\n",
"_________________________________________________________________\n",
"15000/15000 [==============================] - 229s 15ms/step - loss: 1.5385\n"
]
},
{
"data": {
"text/plain": [
"<tensorflow.python.keras.callbacks.History at 0x7fa40c1245e0>"
]
},
"execution_count": 7,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"model = keras.models.Sequential([\n",
" keras.layers.Masking(input_shape=(None,vocab_size)),\n",
" keras.layers.LSTM(128,return_sequences=True),\n",
" keras.layers.Dense(vocab_size,activation='softmax')\n",
"])\n",
"\n",
"model.summary()\n",
"model.compile(loss='categorical_crossentropy')\n",
"\n",
"model.fit(ds_train.batch(8).map(title_batch_fn))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Generarea rezultatului\n",
"\n",
"Acum că am antrenat modelul, vrem să-l folosim pentru a genera un rezultat. În primul rând, avem nevoie de o modalitate de a decoda textul reprezentat de o secvență de numere de tokeni. Pentru aceasta, am putea folosi funcția `tokenizer.sequences_to_texts`; totuși, aceasta nu funcționează bine cu tokenizarea la nivel de caracter. Prin urmare, vom lua un dicționar de tokeni din tokenizer (numit `word_index`), vom construi o hartă inversă și vom scrie propria noastră funcție de decodare:\n"
]
},
{
"cell_type": "code",
"execution_count": 10,
"metadata": {},
"outputs": [],
"source": [
"reverse_map = {val:key for key, val in tokenizer.word_index.items()}\n",
"\n",
"def decode(x):\n",
" return ''.join([reverse_map[t] for t in x])"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Acum, să începem generarea. Vom începe cu un șir `start`, îl vom codifica într-o secvență `inp`, iar apoi, la fiecare pas, vom apela rețeaua noastră pentru a deduce următorul caracter.\n",
"\n",
"Rezultatul rețelei `out` este un vector cu `vocab_size` elemente care reprezintă probabilitățile fiecărui token, iar noi putem găsi numărul tokenului cel mai probabil folosind `argmax`. Apoi, adăugăm acest caracter la lista generată de tokeni și continuăm procesul de generare. Acest proces de generare a unui caracter se repetă de `size` ori pentru a genera numărul necesar de caractere, și ne oprim mai devreme dacă întâlnim `eos_token`.\n"
]
},
{
"cell_type": "code",
"execution_count": 12,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"'Today #39;s lead to strike for the strike for the strike for the strike (AFP)'"
]
},
"execution_count": 12,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"def generate(model,size=100,start='Today '):\n",
" inp = tokenizer.texts_to_sequences([start])[0]\n",
" chars = inp\n",
" for i in range(size):\n",
" out = model(tf.expand_dims(tf.one_hot(inp,vocab_size),0))[0][-1]\n",
" nc = tf.argmax(out)\n",
" if nc==eos_token:\n",
" break\n",
" chars.append(nc.numpy())\n",
" inp = inp+[nc]\n",
" return decode(chars)\n",
" \n",
"generate(model)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Eșantionarea rezultatelor în timpul antrenamentului\n",
"\n",
"Deoarece nu avem metrici utile precum *acuratețea*, singura modalitate prin care putem observa că modelul nostru se îmbunătățește este prin **eșantionarea** șirurilor generate în timpul antrenamentului. Pentru a face acest lucru, vom folosi **callback-uri**, adică funcții pe care le putem transmite funcției `fit` și care vor fi apelate periodic în timpul antrenamentului.\n"
]
},
{
"cell_type": "code",
"execution_count": 13,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Epoch 1/3\n",
"15000/15000 [==============================] - 226s 15ms/step - loss: 1.2703\n",
"Today #39;s a lead in the company for the strike\n",
"Epoch 2/3\n",
"15000/15000 [==============================] - 227s 15ms/step - loss: 1.2057\n",
"Today #39;s the Market Service on Security Start (AP)\n",
"Epoch 3/3\n",
"15000/15000 [==============================] - 226s 15ms/step - loss: 1.1752\n",
"Today #39;s a line on the strike to start for the start\n"
]
},
{
"data": {
"text/plain": [
"<tensorflow.python.keras.callbacks.History at 0x7fa40c74e3d0>"
]
},
"execution_count": 13,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"sampling_callback = keras.callbacks.LambdaCallback(\n",
" on_epoch_end = lambda batch, logs: print(generate(model))\n",
")\n",
"\n",
"model.fit(ds_train.batch(8).map(title_batch_fn),callbacks=[sampling_callback],epochs=3)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Acest exemplu generează deja un text destul de bun, dar poate fi îmbunătățit în mai multe moduri:\n",
"\n",
"* **Mai mult text**. Am folosit doar titluri pentru sarcina noastră, dar poate doriți să experimentați cu text complet. Rețineți că RNN-urile nu sunt foarte bune la gestionarea secvențelor lungi, așa că are sens fie să le împărțiți în propoziții mai scurte, fie să antrenați întotdeauna pe o lungime fixă de secvență cu o valoare predefinită `num_chars` (de exemplu, 256). Puteți încerca să modificați exemplul de mai sus într-o astfel de arhitectură, folosind [tutorialul oficial Keras](https://keras.io/examples/generative/lstm_character_level_text_generation/) ca inspirație.\n",
"\n",
"* **LSTM cu mai multe straturi**. Este logic să încercați 2 sau 3 straturi de celule LSTM. Așa cum am menționat în unitatea anterioară, fiecare strat de LSTM extrage anumite modele din text, iar în cazul generatorului la nivel de caractere, ne putem aștepta ca nivelul inferior al LSTM să fie responsabil pentru extragerea silabelor, iar nivelurile superioare - pentru cuvinte și combinații de cuvinte. Acest lucru poate fi implementat simplu prin transmiterea unui parametru pentru numărul de straturi către constructorul LSTM.\n",
"\n",
"* De asemenea, puteți experimenta cu **unități GRU** și să vedeți care performează mai bine, precum și cu **dimensiuni diferite ale straturilor ascunse**. O dimensiune prea mare a stratului ascuns poate duce la supraînvățare (de exemplu, rețeaua va învăța textul exact), iar o dimensiune mai mică s-ar putea să nu producă rezultate bune.\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Generarea textului soft și temperatura\n",
"\n",
"În definiția anterioară a funcției `generate`, alegeam întotdeauna caracterul cu cea mai mare probabilitate ca următor caracter în textul generat. Acest lucru ducea adesea la faptul că textul \"cicla\" între aceleași secvențe de caractere, din nou și din nou, ca în acest exemplu:\n",
"```\n",
"today of the second the company and a second the company ...\n",
"```\n",
"\n",
"Totuși, dacă analizăm distribuția probabilităților pentru următorul caracter, este posibil ca diferența dintre câteva dintre cele mai mari probabilități să nu fie semnificativă, de exemplu, un caracter poate avea probabilitatea 0.2, iar altul - 0.19, etc. De exemplu, când căutăm următorul caracter în secvența '*play*', următorul caracter poate fi la fel de bine fie un spațiu, fie **e** (ca în cuvântul *player*).\n",
"\n",
"Acest lucru ne conduce la concluzia că nu este întotdeauna \"corect\" să selectăm caracterul cu probabilitatea cea mai mare, deoarece alegerea celui de-al doilea cel mai probabil caracter poate duce tot la un text semnificativ. Este mai înțelept să **eșantionăm** caracterele din distribuția de probabilitate oferită de rezultatul rețelei.\n",
"\n",
"Această eșantionare poate fi realizată folosind funcția `np.multinomial`, care implementează așa-numita **distribuție multinomială**. O funcție care implementează această generare de text **soft** este definită mai jos:\n"
]
},
{
"cell_type": "code",
"execution_count": 33,
"metadata": {
"scrolled": true
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"\n",
"--- Temperature = 0.3\n",
"Today #39;s strike #39; to start at the store return\n",
"On Sunday PO to Be Data Profit Up (Reuters)\n",
"Moscow, SP wins straight to the Microsoft #39;s control of the space start\n",
"President olding of the blast start for the strike to pay &lt;b&gt;...&lt;/b&gt;\n",
"Little red riding hood ficed to the spam countered in European &lt;b&gt;...&lt;/b&gt;\n",
"\n",
"--- Temperature = 0.8\n",
"Today countie strikes ryder missile faces food market blut\n",
"On Sunday collores lose-toppy of sale of Bullment in &lt;b&gt;...&lt;/b&gt;\n",
"Moscow, IBM Diffeiting in Afghan Software Hotels (Reuters)\n",
"President Ol Luster for Profit Peaced Raised (AP)\n",
"Little red riding hood dace on depart talks #39; bank up\n",
"\n",
"--- Temperature = 1.0\n",
"Today wits House buiting debate fixes #39; supervice stake again\n",
"On Sunday arling digital poaching In for level\n",
"Moscow, DS Up 7, Top Proble Protest Caprey Mamarian Strike\n",
"President teps help of roubler stepted lessabul-Dhalitics (AFP)\n",
"Little red riding hood signs on cash in Carter-youb\n",
"\n",
"--- Temperature = 1.3\n",
"Today wits flawer ro, pSIA figat's co DroftwavesIs Talo up\n",
"On Sunday hround elitwing wint EU Powerburlinetien\n",
"Moscow, Bazz #39;s sentries olymen winnelds' next for Olympite Huc?\n",
"President lost securitys from power Elections in Smiltrials\n",
"Little red riding hood vides profit, exponituity, profitmainalist-at said listers\n",
"\n",
"--- Temperature = 1.8\n",
"Today #39;It: He deat: N.KA Asside\n",
"On Sunday i arry Par aldeup patient Wo stele1\n"
]
},
{
"ename": "KeyError",
"evalue": "0",
"output_type": "error",
"traceback": [
"\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
"\u001b[0;31mKeyError\u001b[0m Traceback (most recent call last)",
"\u001b[0;32m<ipython-input-33-db32367a0feb>\u001b[0m in \u001b[0;36m<module>\u001b[0;34m\u001b[0m\n\u001b[1;32m 18\u001b[0m \u001b[0mprint\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34mf\"\\n--- Temperature = {i}\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 19\u001b[0m \u001b[0;32mfor\u001b[0m \u001b[0mj\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mrange\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;36m5\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 20\u001b[0;31m \u001b[0mprint\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mgenerate_soft\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mmodel\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0msize\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;36m300\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0mstart\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mwords\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0mj\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0mtemperature\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mi\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m",
"\u001b[0;32m<ipython-input-33-db32367a0feb>\u001b[0m in \u001b[0;36mgenerate_soft\u001b[0;34m(model, size, start, temperature)\u001b[0m\n\u001b[1;32m 11\u001b[0m \u001b[0mchars\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mappend\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mnc\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 12\u001b[0m \u001b[0minp\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0minp\u001b[0m\u001b[0;34m+\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0mnc\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 13\u001b[0;31m \u001b[0;32mreturn\u001b[0m \u001b[0mdecode\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mchars\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 14\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 15\u001b[0m \u001b[0mwords\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;34m[\u001b[0m\u001b[0;34m'Today '\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m'On Sunday '\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m'Moscow, '\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m'President '\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m'Little red riding hood '\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
"\u001b[0;32m<ipython-input-10-3f5fa6130b1d>\u001b[0m in \u001b[0;36mdecode\u001b[0;34m(x)\u001b[0m\n\u001b[1;32m 2\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 3\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0mdecode\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mx\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 4\u001b[0;31m \u001b[0;32mreturn\u001b[0m \u001b[0;34m''\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mjoin\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0mreverse_map\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0mt\u001b[0m\u001b[0;34m]\u001b[0m \u001b[0;32mfor\u001b[0m \u001b[0mt\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mx\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m",
"\u001b[0;32m<ipython-input-10-3f5fa6130b1d>\u001b[0m in \u001b[0;36m<listcomp>\u001b[0;34m(.0)\u001b[0m\n\u001b[1;32m 2\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 3\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0mdecode\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mx\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 4\u001b[0;31m \u001b[0;32mreturn\u001b[0m \u001b[0;34m''\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mjoin\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0mreverse_map\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0mt\u001b[0m\u001b[0;34m]\u001b[0m \u001b[0;32mfor\u001b[0m \u001b[0mt\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mx\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m",
"\u001b[0;31mKeyError\u001b[0m: 0"
]
}
],
"source": [
"def generate_soft(model,size=100,start='Today ',temperature=1.0):\n",
" inp = tokenizer.texts_to_sequences([start])[0]\n",
" chars = inp\n",
" for i in range(size):\n",
" out = model(tf.expand_dims(tf.one_hot(inp,vocab_size),0))[0][-1]\n",
" probs = tf.exp(tf.math.log(out)/temperature).numpy().astype(np.float64)\n",
" probs = probs/np.sum(probs)\n",
" nc = np.argmax(np.random.multinomial(1,probs,1))\n",
" if nc==eos_token:\n",
" break\n",
" chars.append(nc)\n",
" inp = inp+[nc]\n",
" return decode(chars)\n",
"\n",
"words = ['Today ','On Sunday ','Moscow, ','President ','Little red riding hood ']\n",
" \n",
"for i in [0.3,0.8,1.0,1.3,1.8]:\n",
" print(f\"\\n--- Temperature = {i}\")\n",
" for j in range(5):\n",
" print(generate_soft(model,size=300,start=words[j],temperature=i))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Am introdus încă un parametru numit **temperatură**, care este utilizat pentru a indica cât de strict ar trebui să ne bazăm pe cea mai mare probabilitate. Dacă temperatura este 1.0, facem o eșantionare multinomială corectă, iar când temperatura merge spre infinit - toate probabilitățile devin egale și selectăm aleator următorul caracter. În exemplul de mai jos putem observa că textul devine lipsit de sens atunci când creștem temperatura prea mult și seamănă cu un text \"ciclic\" generat rigid atunci când se apropie de 0.\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"\n---\n\n**Declinare de responsabilitate**: \nAcest document a fost tradus folosind serviciul de traducere AI [Co-op Translator](https://github.com/Azure/co-op-translator). Deși ne străduim să asigurăm acuratețea, vă rugăm să fiți conștienți că traducerile automate pot conține erori sau inexactități. Documentul original în limba sa natală ar trebui considerat sursa autoritară. Pentru informații critice, se recomandă traducerea profesională realizată de un specialist uman. Nu ne asumăm responsabilitatea pentru eventualele neînțelegeri sau interpretări greșite care pot apărea din utilizarea acestei traduceri.\n"
]
}
],
"metadata": {
"interpreter": {
"hash": "16af2a8bbb083ea23e5e41c7f5787656b2ce26968575d8763f2c4b17f9cd711f"
},
"kernelspec": {
"display_name": "Python 3.8.12 ('py38')",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.8.12"
},
"coopTranslator": {
"original_hash": "9fbb7d5fda708537649f71f5f646fcde",
"translation_date": "2025-08-30T00:34:26+00:00",
"source_file": "lessons/5-NLP/17-GenerativeNetworks/GenerativeTF.ipynb",
"language_code": "ro"
}
},
"nbformat": 4,
"nbformat_minor": 4
}

View File

@ -0,0 +1,353 @@
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Mecanisme de atenție și transformatoare\n",
"\n",
"Un dezavantaj major al rețelelor recurente este că toate cuvintele dintr-o secvență au același impact asupra rezultatului. Acest lucru duce la performanțe sub-optime în cazul modelelor standard LSTM encoder-decoder pentru sarcini de tip secvență-la-secvență, cum ar fi Recunoașterea Entităților Numite și Traducerea Automată. În realitate, anumite cuvinte din secvența de intrare au adesea un impact mai mare asupra ieșirilor secvențiale decât altele.\n",
"\n",
"Să luăm în considerare un model de tip secvență-la-secvență, cum ar fi traducerea automată. Acesta este implementat prin două rețele recurente, unde o rețea (**encoder**) comprimă secvența de intrare într-o stare ascunsă, iar cealaltă, **decoder**, desfășoară această stare ascunsă într-un rezultat tradus. Problema cu această abordare este că starea finală a rețelei întâmpină dificultăți în a-și aminti începutul unei propoziții, ceea ce duce la o calitate slabă a modelului pentru propoziții lungi.\n",
"\n",
"**Mecanismele de atenție** oferă o modalitate de a pondera impactul contextual al fiecărui vector de intrare asupra fiecărei predicții de ieșire a RNN-ului. Acest lucru este implementat prin crearea unor scurtături între stările intermediare ale RNN-ului de intrare și RNN-ului de ieșire. Astfel, atunci când generăm simbolul de ieșire $y_t$, vom lua în considerare toate stările ascunse de intrare $h_i$, cu coeficienți de greutate diferiți $\\alpha_{t,i}$.\n",
"\n",
"![Imagine care arată un model encoder/decoder cu un strat de atenție aditiv](../../../../../translated_images/encoder-decoder-attention.7a726296894fb567aa2898c94b17b3289087f6705c11907df8301df9e5eeb3de.ro.png)\n",
"*Modelul encoder-decoder cu mecanism de atenție aditiv din [Bahdanau et al., 2015](https://arxiv.org/pdf/1409.0473.pdf), citat din [acest articol de blog](https://lilianweng.github.io/lil-log/2018/06/24/attention-attention.html)*\n",
"\n",
"Matricea de atenție $\\{\\alpha_{i,j}\\}$ ar reprezenta gradul în care anumite cuvinte de intrare contribuie la generarea unui cuvânt dat în secvența de ieșire. Mai jos este un exemplu al unei astfel de matrice:\n",
"\n",
"![Imagine care arată o aliniere exemplară găsită de RNNsearch-50, preluată din Bahdanau - arviz.org](../../../../../translated_images/bahdanau-fig3.09ba2d37f202a6af11de6c82d2d197830ba5f4528d9ea430eb65fd3a75065973.ro.png)\n",
"\n",
"*Figura preluată din [Bahdanau et al., 2015](https://arxiv.org/pdf/1409.0473.pdf) (Fig.3)*\n",
"\n",
"Mecanismele de atenție sunt responsabile pentru o mare parte din starea actuală sau aproape actuală a artei în procesarea limbajului natural. Totuși, adăugarea atenției crește semnificativ numărul de parametri ai modelului, ceea ce a dus la probleme de scalare cu RNN-urile. O constrângere cheie a scalării RNN-urilor este că natura recurentă a modelelor face dificilă gruparea și paralelizarea antrenării. Într-un RNN, fiecare element al unei secvențe trebuie procesat în ordine secvențială, ceea ce înseamnă că nu poate fi paralelizat cu ușurință.\n",
"\n",
"Adoptarea mecanismelor de atenție combinată cu această constrângere a dus la crearea modelelor Transformatoare, care reprezintă acum starea de artă, pe care le cunoaștem și le folosim astăzi, de la BERT la OpenGPT3.\n",
"\n",
"## Modele transformatoare\n",
"\n",
"În loc să transmită contextul fiecărei predicții anterioare în pasul următor de evaluare, **modelele transformatoare** folosesc **codificări poziționale** și atenție pentru a captura contextul unei intrări date într-o fereastră de text furnizată. Imaginea de mai jos arată cum codificările poziționale cu atenție pot captura contextul într-o fereastră dată.\n",
"\n",
"![GIF animat care arată cum sunt realizate evaluările în modelele transformatoare.](../../../../../lessons/5-NLP/18-Transformers/images/transformer-animated-explanation.gif)\n",
"\n",
"Deoarece fiecare poziție de intrare este mapată independent la fiecare poziție de ieșire, transformatoarele pot paraleliza mai bine decât RNN-urile, ceea ce permite modele de limbaj mult mai mari și mai expresive. Fiecare cap de atenție poate fi utilizat pentru a învăța relații diferite între cuvinte, ceea ce îmbunătățește sarcinile de procesare a limbajului natural.\n",
"\n",
"**BERT** (Bidirectional Encoder Representations from Transformers) este o rețea transformatoare foarte mare, cu mai multe straturi: 12 straturi pentru *BERT-base* și 24 pentru *BERT-large*. Modelul este mai întâi pre-antrenat pe un corpus mare de date text (Wikipedia + cărți) folosind antrenare nesupravegheată (prezicerea cuvintelor mascate într-o propoziție). În timpul pre-antrenării, modelul absoarbe un nivel semnificativ de înțelegere a limbajului, care poate fi apoi valorificat cu alte seturi de date prin ajustare fină. Acest proces se numește **învățare transferabilă**.\n",
"\n",
"![Imagine de pe http://jalammar.github.io/illustrated-bert/](../../../../../translated_images/jalammarBERT-language-modeling-masked-lm.34f113ea5fec4362e39ee4381aab7cad06b5465a0b5f053a0f2aa05fbe14e746.ro.png)\n",
"\n",
"Există multe variații ale arhitecturilor Transformatoare, inclusiv BERT, DistilBERT, BigBird, OpenGPT3 și altele, care pot fi ajustate fin. Pachetul [HuggingFace](https://github.com/huggingface/) oferă un depozit pentru antrenarea multora dintre aceste arhitecturi cu PyTorch.\n",
"\n",
"## Utilizarea BERT pentru clasificarea textului\n",
"\n",
"Să vedem cum putem folosi modelul BERT pre-antrenat pentru a rezolva sarcina noastră tradițională: clasificarea secvențelor. Vom clasifica setul nostru de date original AG News.\n",
"\n",
"Mai întâi, să încărcăm biblioteca HuggingFace și setul nostru de date:\n"
]
},
{
"cell_type": "code",
"execution_count": 10,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Loading dataset...\n",
"Building vocab...\n"
]
}
],
"source": [
"import torch\n",
"import torchtext\n",
"from torchnlp import *\n",
"import transformers\n",
"train_dataset, test_dataset, classes, vocab = load_dataset()\n",
"vocab_len = len(vocab)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Deoarece vom folosi un model BERT pre-antrenat, va fi necesar să utilizăm un tokenizer specific. Mai întâi, vom încărca un tokenizer asociat cu modelul BERT pre-antrenat.\n",
"\n",
"Biblioteca HuggingFace conține un depozit de modele pre-antrenate, pe care le poți utiliza doar specificând numele lor ca argumente pentru funcțiile `from_pretrained`. Toate fișierele binare necesare pentru model vor fi descărcate automat.\n",
"\n",
"Totuși, în anumite situații, va trebui să încarci propriile modele, caz în care poți specifica directorul care conține toate fișierele relevante, inclusiv parametrii pentru tokenizer, fișierul `config.json` cu parametrii modelului, greutățile binare etc.\n"
]
},
{
"cell_type": "code",
"execution_count": 11,
"metadata": {},
"outputs": [],
"source": [
"# To load the model from Internet repository using model name. \n",
"# Use this if you are running from your own copy of the notebooks\n",
"bert_model = 'bert-base-uncased' \n",
"\n",
"# To load the model from the directory on disk. Use this for Microsoft Learn module, because we have\n",
"# prepared all required files for you.\n",
"bert_model = './bert'\n",
"\n",
"tokenizer = transformers.BertTokenizer.from_pretrained(bert_model)\n",
"\n",
"MAX_SEQ_LEN = 128\n",
"PAD_INDEX = tokenizer.convert_tokens_to_ids(tokenizer.pad_token)\n",
"UNK_INDEX = tokenizer.convert_tokens_to_ids(tokenizer.unk_token)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Obiectul `tokenizer` conține funcția `encode` care poate fi utilizată direct pentru a codifica textul:\n"
]
},
{
"cell_type": "code",
"execution_count": 15,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"[101, 1052, 22123, 2953, 2818, 2003, 1037, 2307, 7705, 2005, 17953, 2361, 102]"
]
},
"execution_count": 15,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"tokenizer.encode('PyTorch is a great framework for NLP')"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Apoi, să creăm iteratori pe care îi vom folosi în timpul antrenamentului pentru a accesa datele. Deoarece BERT folosește propria funcție de codificare, va trebui să definim o funcție de umplere similară cu `padify` pe care am definit-o anterior:\n"
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {},
"outputs": [],
"source": [
"def pad_bert(b):\n",
" # b is the list of tuples of length batch_size\n",
" # - first element of a tuple = label, \n",
" # - second = feature (text sequence)\n",
" # build vectorized sequence\n",
" v = [tokenizer.encode(x[1]) for x in b]\n",
" # compute max length of a sequence in this minibatch\n",
" l = max(map(len,v))\n",
" return ( # tuple of two tensors - labels and features\n",
" torch.LongTensor([t[0] for t in b]),\n",
" torch.stack([torch.nn.functional.pad(torch.tensor(t),(0,l-len(t)),mode='constant',value=0) for t in v])\n",
" )\n",
"\n",
"train_loader = torch.utils.data.DataLoader(train_dataset, batch_size=8, collate_fn=pad_bert, shuffle=True)\n",
"test_loader = torch.utils.data.DataLoader(test_dataset, batch_size=8, collate_fn=pad_bert)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"În cazul nostru, vom folosi modelul BERT pre-antrenat numit `bert-base-uncased`. Să încărcăm modelul folosind pachetul `BertForSequenceClassfication`. Acest lucru asigură că modelul nostru are deja o arhitectură necesară pentru clasificare, inclusiv clasificatorul final. Veți vedea un mesaj de avertizare care indică faptul că greutățile clasificatorului final nu sunt inițializate, iar modelul ar necesita pre-antrenare - acest lucru este perfect în regulă, deoarece exact asta urmează să facem!\n"
]
},
{
"cell_type": "code",
"execution_count": 9,
"metadata": {},
"outputs": [
{
"name": "stderr",
"output_type": "stream",
"text": [
"Some weights of the model checkpoint at ./bert were not used when initializing BertForSequenceClassification: ['cls.predictions.bias', 'cls.predictions.transform.dense.weight', 'cls.predictions.transform.dense.bias', 'cls.predictions.decoder.weight', 'cls.seq_relationship.weight', 'cls.seq_relationship.bias', 'cls.predictions.transform.LayerNorm.weight', 'cls.predictions.transform.LayerNorm.bias']\n",
"- This IS expected if you are initializing BertForSequenceClassification from the checkpoint of a model trained on another task or with another architecture (e.g. initializing a BertForSequenceClassification model from a BertForPreTraining model).\n",
"- This IS NOT expected if you are initializing BertForSequenceClassification from the checkpoint of a model that you expect to be exactly identical (initializing a BertForSequenceClassification model from a BertForSequenceClassification model).\n",
"Some weights of BertForSequenceClassification were not initialized from the model checkpoint at ./bert and are newly initialized: ['classifier.weight', 'classifier.bias']\n",
"You should probably TRAIN this model on a down-stream task to be able to use it for predictions and inference.\n"
]
}
],
"source": [
"model = transformers.BertForSequenceClassification.from_pretrained(bert_model,num_labels=4).to(device)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Acum suntem gata să începem antrenamentul! Deoarece BERT este deja pre-antrenat, dorim să începem cu o rată de învățare destul de mică pentru a nu distruge greutățile inițiale.\n",
"\n",
"Toată munca grea este realizată de modelul `BertForSequenceClassification`. Când apelăm modelul pe datele de antrenament, acesta returnează atât pierderea, cât și ieșirea rețelei pentru minibatch-ul de intrare. Folosim pierderea pentru optimizarea parametrilor (`loss.backward()` efectuează trecerea înapoi), iar `out` pentru calcularea acurateței antrenamentului prin compararea etichetelor obținute `labs` (calculate folosind `argmax`) cu etichetele așteptate `labels`.\n",
"\n",
"Pentru a controla procesul, acumulăm pierderea și acuratețea pe parcursul mai multor iterații și le afișăm la fiecare ciclu de antrenament `report_freq`.\n",
"\n",
"Acest antrenament va dura probabil destul de mult timp, așa că limităm numărul de iterații.\n"
]
},
{
"cell_type": "code",
"execution_count": 6,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Loss = 1.1254194641113282, Accuracy = 0.585\n",
"Loss = 0.6194715118408203, Accuracy = 0.83\n",
"Loss = 0.46665248870849607, Accuracy = 0.8475\n",
"Loss = 0.4309701919555664, Accuracy = 0.8575\n",
"Loss = 0.35427074432373046, Accuracy = 0.8825\n",
"Loss = 0.3306886291503906, Accuracy = 0.8975\n",
"Loss = 0.30340143203735354, Accuracy = 0.8975\n",
"Loss = 0.26139299392700194, Accuracy = 0.915\n",
"Loss = 0.26708646774291994, Accuracy = 0.9225\n",
"Loss = 0.3667240524291992, Accuracy = 0.8675\n"
]
}
],
"source": [
"optimizer = torch.optim.Adam(model.parameters(), lr=2e-5)\n",
"\n",
"report_freq = 50\n",
"iterations = 500 # make this larger to train for longer time!\n",
"\n",
"model.train()\n",
"\n",
"i,c = 0,0\n",
"acc_loss = 0\n",
"acc_acc = 0\n",
"\n",
"for labels,texts in train_loader:\n",
" labels = labels.to(device)-1 # get labels in the range 0-3 \n",
" texts = texts.to(device)\n",
" loss, out = model(texts, labels=labels)[:2]\n",
" labs = out.argmax(dim=1)\n",
" acc = torch.mean((labs==labels).type(torch.float32))\n",
" optimizer.zero_grad()\n",
" loss.backward()\n",
" optimizer.step()\n",
" acc_loss += loss\n",
" acc_acc += acc\n",
" i+=1\n",
" c+=1\n",
" if i%report_freq==0:\n",
" print(f\"Loss = {acc_loss.item()/c}, Accuracy = {acc_acc.item()/c}\")\n",
" c = 0\n",
" acc_loss = 0\n",
" acc_acc = 0\n",
" iterations-=1\n",
" if not iterations:\n",
" break"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Poți observa (mai ales dacă mărești numărul de iterații și aștepți suficient de mult) că clasificarea cu BERT ne oferă o acuratețe destul de bună! Acest lucru se datorează faptului că BERT înțelege deja foarte bine structura limbajului, iar noi trebuie doar să ajustăm clasificatorul final. Totuși, deoarece BERT este un model mare, întregul proces de antrenare durează mult și necesită o putere de calcul considerabilă! (GPU, și de preferat mai multe).\n",
"\n",
"> **Note:** În exemplul nostru, am folosit unul dintre cele mai mici modele BERT pre-antrenate. Există modele mai mari care probabil vor oferi rezultate mai bune.\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Evaluarea performanței modelului\n",
"\n",
"Acum putem evalua performanța modelului nostru pe setul de date de testare. Bucla de evaluare este destul de similară cu bucla de antrenare, dar nu trebuie să uităm să comutăm modelul în modul de evaluare apelând `model.eval()`.\n"
]
},
{
"cell_type": "code",
"execution_count": 10,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Final accuracy: 0.9047029702970297\n"
]
}
],
"source": [
"model.eval()\n",
"iterations = 100\n",
"acc = 0\n",
"i = 0\n",
"for labels,texts in test_loader:\n",
" labels = labels.to(device)-1 \n",
" texts = texts.to(device)\n",
" _, out = model(texts, labels=labels)[:2]\n",
" labs = out.argmax(dim=1)\n",
" acc += torch.mean((labs==labels).type(torch.float32))\n",
" i+=1\n",
" if i>iterations: break\n",
" \n",
"print(f\"Final accuracy: {acc.item()/i}\")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Concluzii\n",
"\n",
"În această unitate, am văzut cât de ușor este să luăm un model de limbaj pre-antrenat din biblioteca **transformers** și să-l adaptăm pentru sarcina noastră de clasificare a textului. În mod similar, modelele BERT pot fi utilizate pentru extragerea entităților, răspunsul la întrebări și alte sarcini de procesare a limbajului natural.\n",
"\n",
"Modelele Transformer reprezintă stadiul actual de vârf în NLP, iar în majoritatea cazurilor ar trebui să fie prima soluție cu care începeți să experimentați atunci când implementați soluții personalizate de NLP. Totuși, înțelegerea principiilor de bază ale rețelelor neuronale recurente discutate în acest modul este extrem de importantă dacă doriți să construiți modele neuronale avansate.\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"\n---\n\n**Declinare de responsabilitate**: \nAcest document a fost tradus folosind serviciul de traducere AI [Co-op Translator](https://github.com/Azure/co-op-translator). Deși ne străduim să asigurăm acuratețea, vă rugăm să fiți conștienți că traducerile automate pot conține erori sau inexactități. Documentul original în limba sa natală ar trebui considerat sursa autoritară. Pentru informații critice, se recomandă traducerea profesională realizată de un specialist uman. Nu ne asumăm responsabilitatea pentru eventualele neînțelegeri sau interpretări greșite care pot apărea din utilizarea acestei traduceri.\n"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "py37_pytorch",
"language": "python",
"name": "conda-env-py37_pytorch-py"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.7.7"
},
"coopTranslator": {
"original_hash": "753865967678a92dbce7d7efbd36d980",
"translation_date": "2025-08-30T00:44:40+00:00",
"source_file": "lessons/5-NLP/18-Transformers/TransformersPyTorch.ipynb",
"language_code": "ro"
}
},
"nbformat": 4,
"nbformat_minor": 4
}

View File

@ -0,0 +1,823 @@
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Mecanisme de atenție și transformatoare\n",
"\n",
"Un dezavantaj major al rețelelor recurente este că toate cuvintele dintr-o secvență au același impact asupra rezultatului. Acest lucru duce la o performanță sub-optimală în cazul modelelor standard LSTM encoder-decoder pentru sarcini de tip secvență-la-secvență, cum ar fi Recunoașterea Entităților Numite și Traducerea Automată. În realitate, anumite cuvinte din secvența de intrare au adesea un impact mai mare asupra ieșirilor secvențiale decât altele.\n",
"\n",
"Să luăm în considerare un model secvență-la-secvență, cum ar fi traducerea automată. Acesta este implementat prin două rețele recurente, unde o rețea (**encoder**) comprimă secvența de intrare într-o stare ascunsă, iar cealaltă, **decoder**, desfășoară această stare ascunsă pentru a genera rezultatul tradus. Problema cu această abordare este că starea finală a rețelei întâmpină dificultăți în a-și aminti începutul propoziției, ceea ce duce la o calitate slabă a modelului pentru propoziții lungi.\n",
"\n",
"**Mecanismele de atenție** oferă o modalitate de a pondera impactul contextual al fiecărui vector de intrare asupra fiecărei predicții de ieșire a RNN-ului. Acest lucru este implementat prin crearea unor scurtături între stările intermediare ale RNN-ului de intrare și RNN-ului de ieșire. Astfel, atunci când generăm simbolul de ieșire $y_t$, vom lua în considerare toate stările ascunse de intrare $h_i$, cu coeficienți de greutate diferiți $\\alpha_{t,i}$. \n",
"\n",
"![Imagine care arată un model encoder/decoder cu un strat de atenție aditiv](../../../../../translated_images/encoder-decoder-attention.7a726296894fb567aa2898c94b17b3289087f6705c11907df8301df9e5eeb3de.ro.png)\n",
"*Modelul encoder-decoder cu mecanism de atenție aditiv din [Bahdanau et al., 2015](https://arxiv.org/pdf/1409.0473.pdf), citat din [acest articol de blog](https://lilianweng.github.io/lil-log/2018/06/24/attention-attention.html)*\n",
"\n",
"Matricea de atenție $\\{\\alpha_{i,j}\\}$ reprezintă gradul în care anumite cuvinte de intrare contribuie la generarea unui cuvânt dat în secvența de ieșire. Mai jos este un exemplu al unei astfel de matrici:\n",
"\n",
"![Imagine care arată o aliniere exemplară găsită de RNNsearch-50, preluată din Bahdanau - arviz.org](../../../../../translated_images/bahdanau-fig3.09ba2d37f202a6af11de6c82d2d197830ba5f4528d9ea430eb65fd3a75065973.ro.png)\n",
"\n",
"*Figura preluată din [Bahdanau et al., 2015](https://arxiv.org/pdf/1409.0473.pdf) (Fig.3)*\n",
"\n",
"Mecanismele de atenție sunt responsabile pentru o mare parte din starea actuală sau aproape actuală a artei în procesarea limbajului natural. Totuși, adăugarea atenției crește considerabil numărul de parametri ai modelului, ceea ce a dus la probleme de scalare cu RNN-urile. O constrângere cheie a scalării RNN-urilor este că natura recurentă a modelelor face dificilă gruparea și paralelizarea antrenamentului. Într-un RNN, fiecare element al unei secvențe trebuie procesat în ordine secvențială, ceea ce înseamnă că nu poate fi paralelizat cu ușurință.\n",
"\n",
"Adoptarea mecanismelor de atenție, combinată cu această constrângere, a dus la crearea modelelor Transformatoare, acum considerate Starea de Artă, pe care le cunoaștem și le folosim astăzi, de la BERT la OpenGPT3.\n",
"\n",
"## Modele Transformatoare\n",
"\n",
"În loc să transmită contextul fiecărei predicții anterioare în pasul următor de evaluare, **modelele transformatoare** folosesc **codificări poziționale** și **atenție** pentru a captura contextul unei intrări date într-o fereastră de text furnizată. Imaginea de mai jos arată cum codificările poziționale împreună cu atenția pot captura contextul într-o fereastră dată.\n",
"\n",
"![GIF animat care arată cum sunt realizate evaluările în modelele transformatoare.](../../../../../lessons/5-NLP/18-Transformers/images/transformer-animated-explanation.gif) \n",
"\n",
"Deoarece fiecare poziție de intrare este mapată independent la fiecare poziție de ieșire, transformatoarele pot paraleliza mai bine decât RNN-urile, ceea ce permite modele de limbaj mult mai mari și mai expresive. Fiecare cap de atenție poate fi utilizat pentru a învăța relații diferite între cuvinte, ceea ce îmbunătățește sarcinile de procesare a limbajului natural.\n",
"\n",
"## Construirea unui Model Transformator Simplu\n",
"\n",
"Keras nu conține un strat Transformator încorporat, dar putem construi unul propriu. Ca și înainte, ne vom concentra pe clasificarea textului din setul de date AG News, dar merită menționat că modelele Transformatoare oferă cele mai bune rezultate pentru sarcini NLP mai dificile.\n"
]
},
{
"cell_type": "code",
"execution_count": 1,
"metadata": {},
"outputs": [],
"source": [
"import tensorflow as tf\n",
"from tensorflow import keras\n",
"import tensorflow_datasets as tfds\n",
"import numpy as np\n",
"\n",
"ds_train, ds_test = tfds.load('ag_news_subset').values()\n",
"\n",
"def extract_text(x):\n",
" return x['title']+' '+x['description']\n",
"\n",
"def tupelize(x):\n",
" return (extract_text(x),x['label'])"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Noile straturi în Keras ar trebui să fie subclase ale clasei `Layer` și să implementeze metoda `call`. Să începem cu stratul **Positional Embedding**. Vom folosi [un cod din documentația oficială Keras](https://keras.io/examples/nlp/text_classification_with_transformer/). Vom presupune că umplem toate secvențele de intrare la lungimea `maxlen`.\n"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {},
"outputs": [],
"source": [
"class TokenAndPositionEmbedding(keras.layers.Layer):\n",
" def __init__(self, maxlen, vocab_size, embed_dim):\n",
" super(TokenAndPositionEmbedding, self).__init__()\n",
" self.token_emb = keras.layers.Embedding(input_dim=vocab_size, output_dim=embed_dim)\n",
" self.pos_emb = keras.layers.Embedding(input_dim=maxlen, output_dim=embed_dim)\n",
" self.maxlen = maxlen\n",
"\n",
" def call(self, x):\n",
" maxlen = self.maxlen\n",
" positions = tf.range(start=0, limit=maxlen, delta=1)\n",
" positions = self.pos_emb(positions)\n",
" x = self.token_emb(x)\n",
" return x+positions"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Acest strat constă din două straturi `Embedding`: unul pentru încorporarea token-urilor (în modul pe care l-am discutat anterior) și unul pentru pozițiile token-urilor. Pozițiile token-urilor sunt create ca o secvență de numere naturale de la 0 la `maxlen` folosind `tf.range`, apoi sunt trecute prin stratul de încorporare. Cele două vectori de încorporare rezultați sunt apoi adunați, producând o reprezentare încorporată pozițional a intrării cu forma `maxlen`$\\times$`embed_dim`.\n",
"\n",
"Acum, să implementăm blocul transformer. Acesta va prelua ieșirea stratului de încorporare definit anterior:\n"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {},
"outputs": [],
"source": [
"class TransformerBlock(keras.layers.Layer):\n",
" def __init__(self, embed_dim, num_heads, ff_dim, rate=0.1):\n",
" super(TransformerBlock, self).__init__()\n",
" self.att = keras.layers.MultiHeadAttention(num_heads=num_heads, key_dim=embed_dim, name='attn')\n",
" self.ffn = keras.Sequential(\n",
" [keras.layers.Dense(ff_dim, activation=\"relu\"), keras.layers.Dense(embed_dim),]\n",
" )\n",
" self.layernorm1 = keras.layers.LayerNormalization(epsilon=1e-6)\n",
" self.layernorm2 = keras.layers.LayerNormalization(epsilon=1e-6)\n",
" self.dropout1 = keras.layers.Dropout(rate)\n",
" self.dropout2 = keras.layers.Dropout(rate)\n",
"\n",
" def call(self, inputs, training):\n",
" attn_output = self.att(inputs, inputs)\n",
" attn_output = self.dropout1(attn_output, training=training)\n",
" out1 = self.layernorm1(inputs + attn_output)\n",
" ffn_output = self.ffn(out1)\n",
" ffn_output = self.dropout2(ffn_output, training=training)\n",
" return self.layernorm2(out1 + ffn_output)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Transformer aplică `MultiHeadAttention` pe intrarea codificată pozițional pentru a produce vectorul de atenție cu dimensiunea `maxlen`$\\times$`embed_dim`, care este apoi combinat cu intrarea și normalizat folosind `LayerNormalization`.\n",
"\n",
"> **Notă**: `LayerNormalization` este similar cu `BatchNormalization` discutat în partea *Computer Vision* a acestui parcurs de învățare, dar normalizează ieșirile stratului anterior pentru fiecare eșantion de antrenament în mod independent, pentru a le aduce în intervalul [-1..1].\n",
"\n",
"Ieșirea acestui strat este apoi trecută printr-o rețea `Dense` (în cazul nostru - un perceptron cu două straturi), iar rezultatul este adăugat la ieșirea finală (care este din nou supusă normalizării).\n"
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Model: \"sequential_1\"\n",
"_________________________________________________________________\n",
"Layer (type) Output Shape Param # \n",
"=================================================================\n",
"text_vectorization (TextVect (None, 256) 0 \n",
"_________________________________________________________________\n",
"token_and_position_embedding (None, 256, 32) 648192 \n",
"_________________________________________________________________\n",
"transformer_block (Transform (None, 256, 32) 10656 \n",
"_________________________________________________________________\n",
"global_average_pooling1d (Gl (None, 32) 0 \n",
"_________________________________________________________________\n",
"dropout_2 (Dropout) (None, 32) 0 \n",
"_________________________________________________________________\n",
"dense_2 (Dense) (None, 20) 660 \n",
"_________________________________________________________________\n",
"dropout_3 (Dropout) (None, 20) 0 \n",
"_________________________________________________________________\n",
"dense_3 (Dense) (None, 4) 84 \n",
"=================================================================\n",
"Total params: 659,592\n",
"Trainable params: 659,592\n",
"Non-trainable params: 0\n",
"_________________________________________________________________\n"
]
}
],
"source": [
"embed_dim = 32 # Embedding size for each token\n",
"num_heads = 2 # Number of attention heads\n",
"ff_dim = 32 # Hidden layer size in feed forward network inside transformer\n",
"maxlen = 256\n",
"vocab_size = 20000\n",
"\n",
"model = keras.models.Sequential([\n",
" keras.layers.experimental.preprocessing.TextVectorization(max_tokens=vocab_size,output_sequence_length=maxlen, input_shape=(1,)),\n",
" TokenAndPositionEmbedding(maxlen, vocab_size, embed_dim),\n",
" TransformerBlock(embed_dim, num_heads, ff_dim),\n",
" keras.layers.GlobalAveragePooling1D(),\n",
" keras.layers.Dropout(0.1),\n",
" keras.layers.Dense(20, activation=\"relu\"),\n",
" keras.layers.Dropout(0.1),\n",
" keras.layers.Dense(4, activation=\"softmax\")\n",
"])\n",
"\n",
"model.summary()"
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Training tokenizer\n",
"938/938 [==============================] - 45s 39ms/step - loss: 0.4978 - acc: 0.8068 - val_loss: 0.2808 - val_acc: 0.9124\n"
]
},
{
"data": {
"text/plain": [
"<tensorflow.python.keras.callbacks.History at 0x7f9c2427a0d0>"
]
},
"execution_count": 5,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"print('Training tokenizer')\n",
"model.layers[0].adapt(ds_train.map(extract_text))\n",
"model.compile(loss='sparse_categorical_crossentropy',metrics=['acc'], optimizer='adam')\n",
"model.fit(ds_train.map(tupelize).batch(128),validation_data=ds_test.map(tupelize).batch(128))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Modele Transformer BERT\n",
"\n",
"**BERT** (Bidirectional Encoder Representations from Transformers) este o rețea de transformatoare foarte mare, cu mai multe straturi, având 12 straturi pentru *BERT-base* și 24 pentru *BERT-large*. Modelul este mai întâi pre-antrenat pe un corpus mare de date textuale (Wikipedia + cărți) folosind antrenare nesupravegheată (prezicerea cuvintelor mascate într-o propoziție). În timpul pre-antrenării, modelul dobândește un nivel semnificativ de înțelegere a limbajului, care poate fi apoi valorificat cu alte seturi de date prin ajustare fină. Acest proces se numește **învățare transferabilă**.\n",
"\n",
"![imagine de pe http://jalammar.github.io/illustrated-bert/](../../../../../translated_images/jalammarBERT-language-modeling-masked-lm.34f113ea5fec4362e39ee4381aab7cad06b5465a0b5f053a0f2aa05fbe14e746.ro.png)\n",
"\n",
"Există multe variații ale arhitecturilor Transformer, inclusiv BERT, DistilBERT, BigBird, OpenGPT3 și altele, care pot fi ajustate fin.\n",
"\n",
"Să vedem cum putem folosi un model BERT pre-antrenat pentru a rezolva problema noastră tradițională de clasificare a secvențelor. Vom împrumuta ideea și ceva cod din [documentația oficială](https://www.tensorflow.org/text/tutorials/classify_text_with_bert).\n",
"\n",
"Pentru a încărca modele pre-antrenate, vom folosi **Tensorflow hub**. Mai întâi, să încărcăm vectorizatorul specific BERT:\n"
]
},
{
"cell_type": "code",
"execution_count": 1,
"metadata": {},
"outputs": [
{
"ename": "ModuleNotFoundError",
"evalue": "No module named 'tensorflow_text'",
"output_type": "error",
"traceback": [
"\u001b[1;31m---------------------------------------------------------------------------\u001b[0m",
"\u001b[1;31mModuleNotFoundError\u001b[0m Traceback (most recent call last)",
"\u001b[1;32m~\\AppData\\Local\\Temp/ipykernel_41180/4216669875.py\u001b[0m in \u001b[0;36m<module>\u001b[1;34m\u001b[0m\n\u001b[1;32m----> 1\u001b[1;33m \u001b[1;32mimport\u001b[0m \u001b[0mtensorflow_text\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m\u001b[0;32m 2\u001b[0m \u001b[1;32mimport\u001b[0m \u001b[0mtensorflow_hub\u001b[0m \u001b[1;32mas\u001b[0m \u001b[0mhub\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 3\u001b[0m \u001b[0mvectorizer\u001b[0m \u001b[1;33m=\u001b[0m \u001b[0mhub\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mKerasLayer\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;34m'https://tfhub.dev/tensorflow/bert_en_uncased_preprocess/3'\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n",
"\u001b[1;31mModuleNotFoundError\u001b[0m: No module named 'tensorflow_text'"
]
}
],
"source": [
"import tensorflow_text \n",
"import tensorflow_hub as hub\n",
"vectorizer = hub.KerasLayer('https://tfhub.dev/tensorflow/bert_en_uncased_preprocess/3')"
]
},
{
"cell_type": "code",
"execution_count": 7,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"{'input_type_ids': <tf.Tensor: shape=(1, 128), dtype=int32, numpy=\n",
" array([[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n",
" 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n",
" 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n",
" 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n",
" 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n",
" 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]],\n",
" dtype=int32)>,\n",
" 'input_word_ids': <tf.Tensor: shape=(1, 128), dtype=int32, numpy=\n",
" array([[ 101, 1045, 2293, 19081, 102, 0, 0, 0, 0,\n",
" 0, 0, 0, 0, 0, 0, 0, 0, 0,\n",
" 0, 0, 0, 0, 0, 0, 0, 0, 0,\n",
" 0, 0, 0, 0, 0, 0, 0, 0, 0,\n",
" 0, 0, 0, 0, 0, 0, 0, 0, 0,\n",
" 0, 0, 0, 0, 0, 0, 0, 0, 0,\n",
" 0, 0, 0, 0, 0, 0, 0, 0, 0,\n",
" 0, 0, 0, 0, 0, 0, 0, 0, 0,\n",
" 0, 0, 0, 0, 0, 0, 0, 0, 0,\n",
" 0, 0, 0, 0, 0, 0, 0, 0, 0,\n",
" 0, 0, 0, 0, 0, 0, 0, 0, 0,\n",
" 0, 0, 0, 0, 0, 0, 0, 0, 0,\n",
" 0, 0, 0, 0, 0, 0, 0, 0, 0,\n",
" 0, 0, 0, 0, 0, 0, 0, 0, 0,\n",
" 0, 0]], dtype=int32)>,\n",
" 'input_mask': <tf.Tensor: shape=(1, 128), dtype=int32, numpy=\n",
" array([[1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n",
" 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n",
" 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n",
" 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n",
" 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n",
" 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]],\n",
" dtype=int32)>}"
]
},
"execution_count": 7,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"vectorizer(['I love transformers'])"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Este important să folosești același vectorizator ca cel pe care a fost antrenată rețeaua originală. De asemenea, vectorizatorul BERT returnează trei componente:\n",
"* `input_word_ids`, care este o secvență de numere de tokeni pentru propoziția de intrare\n",
"* `input_mask`, care indică ce parte a secvenței conține intrarea reală și care parte este umplere. Este similar cu masca produsă de stratul `Masking`\n",
"* `input_type_ids` este utilizat pentru sarcini de modelare a limbajului și permite specificarea a două propoziții de intrare într-o singură secvență.\n",
"\n",
"Apoi, putem instanția extractorul de caracteristici BERT:\n"
]
},
{
"cell_type": "code",
"execution_count": 8,
"metadata": {},
"outputs": [],
"source": [
"bert = hub.KerasLayer('https://tfhub.dev/tensorflow/small_bert/bert_en_uncased_L-4_H-128_A-2/1')"
]
},
{
"cell_type": "code",
"execution_count": 9,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"pooled_output -> (1, 128)\n",
"encoder_outputs -> 4\n",
"sequence_output -> (1, 128, 128)\n",
"default -> (1, 128)\n"
]
}
],
"source": [
"z = bert(vectorizer(['I love transformers']))\n",
"for i,x in z.items():\n",
" print(f\"{i} -> { len(x) if isinstance(x, list) else x.shape }\")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Așadar, stratul BERT returnează o serie de rezultate utile:\n",
"* `pooled_output` este rezultatul mediei tuturor token-urilor din secvență. Îl poți considera ca o reprezentare semantică inteligentă a întregii rețele. Este echivalent cu ieșirea stratului `GlobalAveragePooling1D` din modelul nostru anterior.\n",
"* `sequence_output` este ieșirea ultimului strat transformer (corespunde ieșirii `TransformerBlock` din modelul nostru de mai sus).\n",
"* `encoder_outputs` sunt ieșirile tuturor straturilor transformer. Deoarece am încărcat un model BERT cu 4 straturi (așa cum probabil îți dai seama din nume, care conține `4_H`), acesta are 4 tensori. Ultimul este același cu `sequence_output`.\n",
"\n",
"Acum vom defini modelul de clasificare end-to-end. Vom folosi *definirea funcțională a modelului*, în care definim intrarea modelului și apoi oferim o serie de expresii pentru a calcula ieșirea acestuia. De asemenea, vom face ca greutățile modelului BERT să nu fie antrenabile și vom antrena doar clasificatorul final:\n"
]
},
{
"cell_type": "code",
"execution_count": 10,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Model: \"model\"\n",
"__________________________________________________________________________________________________\n",
"Layer (type) Output Shape Param # Connected to \n",
"==================================================================================================\n",
"input_1 (InputLayer) [(None,)] 0 \n",
"__________________________________________________________________________________________________\n",
"keras_layer (KerasLayer) {'input_type_ids': ( 0 input_1[0][0] \n",
"__________________________________________________________________________________________________\n",
"keras_layer_1 (KerasLayer) {'pooled_output': (N 4782465 keras_layer[0][0] \n",
" keras_layer[0][1] \n",
" keras_layer[0][2] \n",
"__________________________________________________________________________________________________\n",
"dropout_4 (Dropout) (None, 128) 0 keras_layer_1[0][5] \n",
"__________________________________________________________________________________________________\n",
"dense_4 (Dense) (None, 4) 516 dropout_4[0][0] \n",
"==================================================================================================\n",
"Total params: 4,782,981\n",
"Trainable params: 516\n",
"Non-trainable params: 4,782,465\n",
"__________________________________________________________________________________________________\n"
]
}
],
"source": [
"inp = keras.Input(shape=(),dtype=tf.string)\n",
"x = vectorizer(inp)\n",
"x = bert(x)\n",
"x = keras.layers.Dropout(0.1)(x['pooled_output'])\n",
"out = keras.layers.Dense(4,activation='softmax')(x)\n",
"model = keras.models.Model(inp,out)\n",
"bert.trainable = False\n",
"model.summary()"
]
},
{
"cell_type": "code",
"execution_count": 11,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"938/938 [==============================] - 528s 559ms/step - loss: 0.8056 - acc: 0.6983 - val_loss: 0.5953 - val_acc: 0.7888\n"
]
},
{
"data": {
"text/plain": [
"<tensorflow.python.keras.callbacks.History at 0x7f9bb1e36d00>"
]
},
"execution_count": 11,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"model.compile(loss='sparse_categorical_crossentropy',metrics=['acc'], optimizer='adam')\n",
"model.fit(ds_train.map(tupelize).batch(128),validation_data=ds_test.map(tupelize).batch(128))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Deși există puțini parametri antrenabili, procesul este destul de lent, deoarece extractorul de caracteristici BERT este foarte solicitant din punct de vedere computațional. Se pare că nu am reușit să obținem o acuratețe rezonabilă, fie din cauza lipsei de antrenament, fie din cauza lipsei de parametri ai modelului.\n",
"\n",
"Să încercăm să deblocăm greutățile BERT și să le antrenăm și pe acestea. Acest lucru necesită o rată de învățare foarte mică și, de asemenea, o strategie de antrenament mai atentă, cu **warmup**, folosind optimizatorul **AdamW**. Vom folosi pachetul `tf-models-official` pentru a crea optimizatorul:\n"
]
},
{
"cell_type": "code",
"execution_count": 12,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Model: \"model\"\n",
"__________________________________________________________________________________________________\n",
"Layer (type) Output Shape Param # Connected to \n",
"==================================================================================================\n",
"input_1 (InputLayer) [(None,)] 0 \n",
"__________________________________________________________________________________________________\n",
"keras_layer (KerasLayer) {'input_type_ids': ( 0 input_1[0][0] \n",
"__________________________________________________________________________________________________\n",
"keras_layer_1 (KerasLayer) {'pooled_output': (N 4782465 keras_layer[0][0] \n",
" keras_layer[0][1] \n",
" keras_layer[0][2] \n",
"__________________________________________________________________________________________________\n",
"dropout_4 (Dropout) (None, 128) 0 keras_layer_1[0][5] \n",
"__________________________________________________________________________________________________\n",
"dense_4 (Dense) (None, 4) 516 dropout_4[0][0] \n",
"==================================================================================================\n",
"Total params: 4,782,981\n",
"Trainable params: 4,782,980\n",
"Non-trainable params: 1\n",
"__________________________________________________________________________________________________\n",
"938/938 [==============================] - 629s 664ms/step - loss: 0.6344 - acc: 0.7658 - val_loss: 0.4876 - val_acc: 0.8247\n"
]
},
{
"data": {
"text/plain": [
"<tensorflow.python.keras.callbacks.History at 0x7f9bb0bd0070>"
]
},
"execution_count": 12,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"from official.nlp import optimization \n",
"bert.trainable=True\n",
"model.summary()\n",
"epochs = 3\n",
"opt = optimization.create_optimizer(\n",
" init_lr=3e-5,\n",
" num_train_steps=epochs*len(ds_train),\n",
" num_warmup_steps=0.1*epochs*len(ds_train),\n",
" optimizer_type='adamw')\n",
"\n",
"model.compile(loss='sparse_categorical_crossentropy',metrics=['acc'], optimizer=opt)\n",
"model.fit(ds_train.map(tupelize).batch(128),validation_data=ds_test.map(tupelize).batch(128))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Așa cum se poate observa, antrenamentul merge destul de încet - dar s-ar putea să vrei să experimentezi și să antrenezi modelul pentru câteva epoci (5-10) și să vezi dacă poți obține cel mai bun rezultat comparativ cu abordările pe care le-am folosit anterior.\n",
"\n",
"## Biblioteca Huggingface Transformers\n",
"\n",
"O altă modalitate foarte comună (și puțin mai simplă) de a utiliza modelele Transformer este [pachetul HuggingFace](https://github.com/huggingface/), care oferă blocuri simple pentru diferite sarcini NLP. Este disponibil atât pentru Tensorflow, cât și pentru PyTorch, un alt cadru foarte popular pentru rețele neuronale.\n",
"\n",
"> **Notă**: Dacă nu ești interesat să vezi cum funcționează biblioteca Transformers - poți sări direct la finalul acestui notebook, deoarece nu vei vedea nimic substanțial diferit față de ceea ce am făcut mai sus. Vom repeta aceiași pași de antrenare a modelului BERT folosind o bibliotecă diferită și un model substanțial mai mare. Prin urmare, procesul implică un antrenament destul de lung, așa că s-ar putea să vrei doar să te uiți peste cod.\n",
"\n",
"Să vedem cum poate fi rezolvată problema noastră folosind [Huggingface Transformers](http://huggingface.co).\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Primul lucru pe care trebuie să-l facem este să alegem modelul pe care îl vom folosi. Pe lângă câteva modele integrate, Huggingface conține un [repository online de modele](https://huggingface.co/models), unde poți găsi multe alte modele pre-antrenate de comunitate. Toate aceste modele pot fi încărcate și utilizate doar prin furnizarea unui nume de model. Toate fișierele binare necesare pentru model vor fi descărcate automat.\n",
"\n",
"În anumite situații, va trebui să încarci propriile tale modele, caz în care poți specifica directorul care conține toate fișierele relevante, inclusiv parametrii pentru tokenizer, fișierul `config.json` cu parametrii modelului, greutățile binare etc.\n",
"\n",
"Pornind de la numele modelului, putem inițializa atât modelul, cât și tokenizer-ul. Să începem cu un tokenizer:\n"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {},
"outputs": [],
"source": [
"import transformers\n",
"\n",
"# To load the model from Internet repository using model name. \n",
"# Use this if you are running from your own copy of the notebooks\n",
"bert_model = 'bert-base-uncased' \n",
"\n",
"# To load the model from the directory on disk. Use this for Microsoft Learn module, because we have\n",
"# prepared all required files for you.\n",
"#bert_model = './bert'\n",
"\n",
"tokenizer = transformers.BertTokenizer.from_pretrained(bert_model)\n",
"\n",
"MAX_SEQ_LEN = 128\n",
"PAD_INDEX = tokenizer.convert_tokens_to_ids(tokenizer.pad_token)\n",
"UNK_INDEX = tokenizer.convert_tokens_to_ids(tokenizer.unk_token)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Obiectul `tokenizer` conține funcția `encode` care poate fi utilizată direct pentru a codifica textul:\n"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"[101, 23435, 12314, 2003, 1037, 2307, 7705, 2005, 17953, 2361, 102]"
]
},
"execution_count": 3,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"tokenizer.encode('Tensorflow is a great framework for NLP')"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Putem folosi, de asemenea, tokenizer-ul pentru a codifica o secvență într-un mod potrivit pentru a fi transmis modelului, adică incluzând câmpurile `token_ids`, `input_mask`, etc. De asemenea, putem specifica că dorim tensori Tensorflow prin furnizarea argumentului `return_tensors='tf'`:\n"
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"{'input_ids': <tf.Tensor: shape=(1, 5), dtype=int32, numpy=array([[ 101, 7592, 1010, 2045, 102]], dtype=int32)>, 'token_type_ids': <tf.Tensor: shape=(1, 5), dtype=int32, numpy=array([[0, 0, 0, 0, 0]], dtype=int32)>, 'attention_mask': <tf.Tensor: shape=(1, 5), dtype=int32, numpy=array([[1, 1, 1, 1, 1]], dtype=int32)>}"
]
},
"execution_count": 4,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"tokenizer(['Hello, there'],return_tensors='tf')"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"În cazul nostru, vom folosi un model BERT pre-antrenat numit `bert-base-uncased`. *Uncased* indică faptul că modelul este insensibil la majuscule și minuscule.\n",
"\n",
"Când antrenăm modelul, trebuie să furnizăm o secvență tokenizată ca intrare, așa că vom proiecta un flux de procesare a datelor. Deoarece `tokenizer.encode` este o funcție Python, vom folosi aceeași abordare ca în unitatea anterioară, apelând-o cu ajutorul `py_function`:\n"
]
},
{
"cell_type": "code",
"execution_count": 31,
"metadata": {},
"outputs": [],
"source": [
"def process(x):\n",
" return tokenizer.encode(x.numpy().decode('utf-8'),return_tensors='tf',padding='max_length',max_length=MAX_SEQ_LEN,truncation=True)[0]\n",
"\n",
"def process_fn(x):\n",
" s = x['title']+' '+x['description']\n",
" e = tf.py_function(process,inp=[s],Tout=(tf.int32))\n",
" e.set_shape(MAX_SEQ_LEN)\n",
" return e,x['label']"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Acum putem încărca modelul actual folosind pachetul `BertForSequenceClassification`. Acest lucru asigură că modelul nostru are deja o arhitectură necesară pentru clasificare, inclusiv clasificatorul final. Veți vedea un mesaj de avertizare care indică faptul că greutățile clasificatorului final nu sunt inițializate, iar modelul ar necesita pre-antrenare - acest lucru este perfect în regulă, deoarece exact asta urmează să facem!\n"
]
},
{
"cell_type": "code",
"execution_count": 32,
"metadata": {},
"outputs": [],
"source": [
"model = transformers.TFBertForSequenceClassification.from_pretrained(bert_model,num_labels=4,output_attentions=False)"
]
},
{
"cell_type": "code",
"execution_count": 33,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Model: \"tf_bert_for_sequence_classification_1\"\n",
"_________________________________________________________________\n",
"Layer (type) Output Shape Param # \n",
"=================================================================\n",
"bert (TFBertMainLayer) multiple 109482240 \n",
"_________________________________________________________________\n",
"dropout_75 (Dropout) multiple 0 \n",
"_________________________________________________________________\n",
"classifier (Dense) multiple 3076 \n",
"=================================================================\n",
"Total params: 109,485,316\n",
"Trainable params: 109,485,316\n",
"Non-trainable params: 0\n",
"_________________________________________________________________\n"
]
}
],
"source": [
"model.summary()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Așa cum puteți vedea din `summary()`, modelul conține aproape 110 milioane de parametri! Probabil, dacă dorim o sarcină simplă de clasificare pe un set de date relativ mic, nu dorim să antrenăm stratul de bază BERT:\n"
]
},
{
"cell_type": "code",
"execution_count": 34,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Model: \"tf_bert_for_sequence_classification_1\"\n",
"_________________________________________________________________\n",
"Layer (type) Output Shape Param # \n",
"=================================================================\n",
"bert (TFBertMainLayer) multiple 109482240 \n",
"_________________________________________________________________\n",
"dropout_75 (Dropout) multiple 0 \n",
"_________________________________________________________________\n",
"classifier (Dense) multiple 3076 \n",
"=================================================================\n",
"Total params: 109,485,316\n",
"Trainable params: 3,076\n",
"Non-trainable params: 109,482,240\n",
"_________________________________________________________________\n"
]
}
],
"source": [
"model.layers[0].trainable = False\n",
"model.summary()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Acum suntem gata să începem antrenamentul!\n",
"\n",
"> **Notă**: Antrenarea unui model BERT la scară completă poate fi foarte consumatoare de timp! Prin urmare, vom antrena modelul doar pentru primele 32 de loturi. Acesta este doar un exemplu pentru a arăta cum se configurează antrenamentul modelului. Dacă ești interesat să încerci antrenamentul la scară completă - elimină pur și simplu parametrii `steps_per_epoch` și `validation_steps`, și pregătește-te să aștepți!\n"
]
},
{
"cell_type": "code",
"execution_count": 30,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"32/32 [==============================] - 142s 4s/step - loss: 1.3896 - acc: 0.2500 - val_loss: 1.3863 - val_acc: 0.2480\n"
]
},
{
"data": {
"text/plain": [
"<tensorflow.python.keras.callbacks.History at 0x7f1d40a4b6a0>"
]
},
"execution_count": 30,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"model.compile('adam','sparse_categorical_crossentropy',['acc'])\n",
"tf.get_logger().setLevel('ERROR')\n",
"model.fit(ds_train.map(process_fn).batch(32),validation_data=ds_test.map(process_fn).batch(32),steps_per_epoch=32,validation_steps=2)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Dacă mărești numărul de iterații, aștepți suficient și antrenezi pentru mai multe epoci, te poți aștepta ca clasificarea cu BERT să ne ofere cea mai bună acuratețe! Asta deoarece BERT înțelege deja destul de bine structura limbajului, iar noi trebuie doar să ajustăm clasificatorul final. Totuși, deoarece BERT este un model mare, întregul proces de antrenare durează mult și necesită o putere de calcul considerabilă! (GPU, și de preferat mai multe).\n",
"\n",
"> **Note:** În exemplul nostru, am folosit unul dintre cele mai mici modele BERT pre-antrenate. Există modele mai mari care probabil vor oferi rezultate mai bune.\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Concluzii\n",
"\n",
"În această unitate, am analizat arhitecturi de modele foarte recente bazate pe **transformers**. Le-am aplicat pentru sarcina noastră de clasificare a textului, dar, în mod similar, modelele BERT pot fi utilizate pentru extragerea entităților, răspunsuri la întrebări și alte sarcini de procesare a limbajului natural.\n",
"\n",
"Modelele transformer reprezintă vârful de performanță actual în NLP, iar în majoritatea cazurilor ar trebui să fie prima soluție pe care începeți să o experimentați atunci când implementați soluții personalizate de NLP. Totuși, înțelegerea principiilor de bază ale rețelelor neuronale recurente discutate în acest modul este extrem de importantă dacă doriți să construiți modele neuronale avansate.\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": []
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"\n---\n\n**Declinare de responsabilitate**: \nAcest document a fost tradus folosind serviciul de traducere AI [Co-op Translator](https://github.com/Azure/co-op-translator). Deși ne străduim să asigurăm acuratețea, vă rugăm să fiți conștienți că traducerile automate pot conține erori sau inexactități. Documentul original în limba sa natală ar trebui considerat sursa autoritară. Pentru informații critice, se recomandă traducerea profesională realizată de un specialist uman. Nu ne asumăm responsabilitatea pentru eventualele neînțelegeri sau interpretări greșite care pot apărea din utilizarea acestei traduceri.\n"
]
}
],
"metadata": {
"interpreter": {
"hash": "0cb620c6d4b9f7a635928804c26cf22403d89d98d79684e4529119355ee6d5a5"
},
"kernelspec": {
"display_name": "py38_tensorflow",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.8.12"
},
"coopTranslator": {
"original_hash": "ab59c532409774988ab875f2260e8e53",
"translation_date": "2025-08-30T00:48:27+00:00",
"source_file": "lessons/5-NLP/18-Transformers/TransformersTF.ipynb",
"language_code": "ro"
}
},
"nbformat": 4,
"nbformat_minor": 4
}

Some files were not shown because too many files have changed in this diff Show More