🌐 Update translations via Co-op Translator

This commit is contained in:
leestott 2025-08-30 08:19:54 +00:00 committed by GitHub
parent f170ba187e
commit b5a91026ce
110 changed files with 100540 additions and 127 deletions

View File

@ -1,8 +1,8 @@
<!--
CO_OP_TRANSLATOR_METADATA:
{
"original_hash": "f3a6b0ddf7e6e3f33b2a543baf086dc9",
"translation_date": "2025-08-25T21:15:38+00:00",
"original_hash": "07191303b7ea2aff1d47e2b0fe4bb862",
"translation_date": "2025-08-30T07:04:46+00:00",
"source_file": "README.md",
"language_code": "hr"
}
@ -23,11 +23,22 @@ CO_OP_TRANSLATOR_METADATA:
# Umjetna inteligencija za početnike - Kurikulum
|![ Sketchnote by [(@girlie_mac)](https://twitter.com/girlie_mac) ](./lessons/sketchnotes/ai-overview.png)|
|![Sketchnote by @girlie_mac https://twitter.com/girlie_mac](../../translated_images/ai-overview.0857791951d19500d0ef8b803d77110c738dcafc52306e6d68724742cd4af167.hr.png)|
|:---:|
| AI za početnike - _Sketchnote by [@girlie_mac](https://twitter.com/girlie_mac)_ |
| AI za početnike - _Sketchnote od [@girlie_mac](https://twitter.com/girlie_mac)_ |
Istražite svijet **umjetne inteligencije** (AI) uz naš 12-tjedni kurikulum od 24 lekcije! Uključuje praktične lekcije, kvizove i laboratorijske vježbe. Kurikulum je prilagođen početnicima i pokriva alate poput TensorFlow-a i PyTorch-a, kao i etiku u AI.
Istražite svijet **umjetne inteligencije** (AI) uz naš 12-tjedni kurikulum s 24 lekcije! Uključuje praktične lekcije, kvizove i laboratorijske vježbe. Kurikulum je prilagođen početnicima i pokriva alate poput TensorFlowa i PyTorcha, kao i etiku u AI-ju.
### 🌐 Podrška za više jezika
#### Podržano putem GitHub Action (Automatizirano i uvijek ažurirano)
[Francuski](../fr/README.md) | [Španjolski](../es/README.md) | [Njemački](../de/README.md) | [Ruski](../ru/README.md) | [Arapski](../ar/README.md) | [Perzijski (Farsi)](../fa/README.md) | [Urdu](../ur/README.md) | [Kineski (pojednostavljeni)](../zh/README.md) | [Kineski (tradicionalni, Makao)](../mo/README.md) | [Kineski (tradicionalni, Hong Kong)](../hk/README.md) | [Kineski (tradicionalni, Tajvan)](../tw/README.md) | [Japanski](../ja/README.md) | [Korejski](../ko/README.md) | [Hindski](../hi/README.md) | [Bengalski](../bn/README.md) | [Marathi](../mr/README.md) | [Nepalski](../ne/README.md) | [Pandžapski (Gurmukhi)](../pa/README.md) | [Portugalski (Portugal)](../pt/README.md) | [Portugalski (Brazil)](../br/README.md) | [Talijanski](../it/README.md) | [Poljski](../pl/README.md) | [Turski](../tr/README.md) | [Grčki](../el/README.md) | [Tajlandski](../th/README.md) | [Švedski](../sv/README.md) | [Danski](../da/README.md) | [Norveški](../no/README.md) | [Finski](../fi/README.md) | [Nizozemski](../nl/README.md) | [Hebrejski](../he/README.md) | [Vijetnamski](../vi/README.md) | [Indonezijski](../id/README.md) | [Malajski](../ms/README.md) | [Tagalog (Filipinski)](../tl/README.md) | [Svahili](../sw/README.md) | [Mađarski](../hu/README.md) | [Češki](../cs/README.md) | [Slovački](../sk/README.md) | [Rumunjski](../ro/README.md) | [Bugarski](../bg/README.md) | [Srpski (ćirilica)](../sr/README.md) | [Hrvatski](./README.md) | [Slovenski](../sl/README.md) | [Ukrajinski](../uk/README.md) | [Burmanski (Mjanmar)](../my/README.md)
**Ako želite dodati dodatne jezike, podržani jezici navedeni su [ovdje](https://github.com/Azure/co-op-translator/blob/main/getting_started/supported-languages.md)**
## Pridružite se zajednici
[![Azure AI Discord](https://dcbadge.limes.pink/api/server/kzRShWzttr)](https://discord.gg/kzRShWzttr)
## Što ćete naučiti
@ -35,74 +46,74 @@ Istražite svijet **umjetne inteligencije** (AI) uz naš 12-tjedni kurikulum od
U ovom kurikulumu naučit ćete:
* Različite pristupe umjetnoj inteligenciji, uključujući "dobri stari" simbolički pristup s **predstavljanjem znanja** i zaključivanjem ([GOFAI](https://en.wikipedia.org/wiki/Symbolic_artificial_intelligence)).
* **Neuralne mreže** i **duboko učenje**, koji su srž moderne AI. Ilustrirat ćemo koncepte iza ovih važnih tema koristeći kod u dva najpopularnija okvira - [TensorFlow](http://Tensorflow.org) i [PyTorch](http://pytorch.org).
* **Neuralne arhitekture** za rad s slikama i tekstom. Pokrit ćemo novije modele, ali možda nećemo obuhvatiti najnovije dostignuće.
* Različite pristupe umjetnoj inteligenciji, uključujući "dobri stari" simbolički pristup s **reprezentacijom znanja** i zaključivanjem ([GOFAI](https://en.wikipedia.org/wiki/Symbolic_artificial_intelligence)).
* **Neuronske mreže** i **duboko učenje**, koji su u središtu moderne AI tehnologije. Koncepte iza ovih važnih tema ilustrirat ćemo kodom u dva najpopularnija okvira - [TensorFlow](http://Tensorflow.org) i [PyTorch](http://pytorch.org).
* **Neuronske arhitekture** za rad sa slikama i tekstom. Pokrit ćemo nedavne modele, ali možda nećemo obuhvatiti najnovije stanje tehnologije.
* Manje popularne AI pristupe, poput **genetskih algoritama** i **sustava s više agenata**.
Što nećemo pokriti u ovom kurikulumu:
> [Pronađite sve dodatne resurse za ovaj tečaj u našoj Microsoft Learn kolekciji](https://learn.microsoft.com/en-us/collections/7w28iy2xrqzdj0?WT.mc_id=academic-77998-bethanycheum)
* Poslovne slučajeve korištenja **AI u poslovanju**. Razmislite o pohađanju [Uvod u AI za poslovne korisnike](https://docs.microsoft.com/learn/paths/introduction-ai-for-business-users/?WT.mc_id=academic-77998-bethanycheum) na Microsoft Learn, ili [AI Business School](https://www.microsoft.com/ai/ai-business-school/?WT.mc_id=academic-77998-bethanycheum), razvijen u suradnji s [INSEAD](https://www.insead.edu/).
* Poslovne slučajeve korištenja **AI u poslovanju**. Razmotrite pohađanje [Uvod u AI za poslovne korisnike](https://docs.microsoft.com/learn/paths/introduction-ai-for-business-users/?WT.mc_id=academic-77998-bethanycheum) na Microsoft Learn platformi ili [AI Business School](https://www.microsoft.com/ai/ai-business-school/?WT.mc_id=academic-77998-bethanycheum), razvijene u suradnji s [INSEAD](https://www.insead.edu/).
* **Klasično strojno učenje**, koje je dobro opisano u našem [Kurikulumu za strojno učenje za početnike](http://github.com/Microsoft/ML-for-Beginners).
* Praktične AI aplikacije izgrađene koristeći **[Cognitive Services](https://azure.microsoft.com/services/cognitive-services/?WT.mc_id=academic-77998-bethanycheum)**. Za ovo preporučujemo da započnete s modulima Microsoft Learn za [viziju](https://docs.microsoft.com/learn/paths/create-computer-vision-solutions-azure-cognitive-services/?WT.mc_id=academic-77998-bethanycheum), [obradu prirodnog jezika](https://docs.microsoft.com/learn/paths/explore-natural-language-processing/?WT.mc_id=academic-77998-bethanycheum), **[Generativni AI s Azure OpenAI uslugom](https://learn.microsoft.com/en-us/training/paths/develop-ai-solutions-azure-openai/?WT.mc_id=academic-77998-bethanycheum)** i druge.
* Specifične ML **Cloud Frameworks**, poput [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), ili [Azure Databricks](https://docs.microsoft.com/learn/paths/data-engineer-azure-databricks?WT.mc_id=academic-77998-bethanycheum). Razmislite o korištenju [Izgradnja i upravljanje rješenjima za strojno učenje s Azure Machine Learning](https://docs.microsoft.com/learn/paths/build-ai-solutions-with-azure-ml-service/?WT.mc_id=academic-77998-bethanycheum) i [Izgradnja i upravljanje rješenjima za strojno učenje s Azure Databricks](https://docs.microsoft.com/learn/paths/build-operate-machine-learning-solutions-azure-databricks/?WT.mc_id=academic-77998-bethanycheum) kurikuluma.
* **Konverzacijski AI** i **Chat Botovi**. Postoji zaseban [Izradite rješenja za konverzacijski AI](https://docs.microsoft.com/learn/paths/create-conversational-ai-solutions/?WT.mc_id=academic-77998-bethanycheum) kurikulum, a možete se također referirati na [ovaj blog post](https://soshnikov.com/azure/hello-bot-conversational-ai-on-microsoft-platform/) za više detalja.
* **Duboka matematika** iza dubokog učenja. Za ovo bismo preporučili [Deep Learning](https://www.amazon.com/Deep-Learning-Adaptive-Computation-Machine/dp/0262035618) od Iana Goodfellowa, Yoshue Bengioa i Aarona Courvillea, koja je također dostupna online na [https://www.deeplearningbook.org/](https://www.deeplearningbook.org/).
* Praktične AI aplikacije izgrađene pomoću **[Cognitive Services](https://azure.microsoft.com/services/cognitive-services/?WT.mc_id=academic-77998-bethanycheum)**. Za ovo preporučujemo početak s modulima Microsoft Learn za [viziju](https://docs.microsoft.com/learn/paths/create-computer-vision-solutions-azure-cognitive-services/?WT.mc_id=academic-77998-bethanycheum), [obradu prirodnog jezika](https://docs.microsoft.com/learn/paths/explore-natural-language-processing/?WT.mc_id=academic-77998-bethanycheum), **[Generativni AI s Azure OpenAI Service](https://learn.microsoft.com/en-us/training/paths/develop-ai-solutions-azure-openai/?WT.mc_id=academic-77998-bethanycheum)** i druge.
* Specifične ML **Cloud Frameworks**, poput [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) ili [Azure Databricks](https://docs.microsoft.com/learn/paths/data-engineer-azure-databricks?WT.mc_id=academic-77998-bethanycheum). Razmotrite korištenje [Izgradnja i upravljanje rješenjima za strojno učenje s Azure Machine Learning](https://docs.microsoft.com/learn/paths/build-ai-solutions-with-azure-ml-service/?WT.mc_id=academic-77998-bethanycheum) i [Izgradnja i upravljanje rješenjima za strojno učenje s Azure Databricks](https://docs.microsoft.com/learn/paths/build-operate-machine-learning-solutions-azure-databricks/?WT.mc_id=academic-77998-bethanycheum).
* **Konverzacijski AI** i **Chat Botovi**. Postoji zaseban [Kreiranje rješenja za konverzacijski AI](https://docs.microsoft.com/learn/paths/create-conversational-ai-solutions/?WT.mc_id=academic-77998-bethanycheum) tečaj, a možete se također referirati na [ovaj blog post](https://soshnikov.com/azure/hello-bot-conversational-ai-on-microsoft-platform/) za više detalja.
* **Duboka matematika** iza dubokog učenja. Za ovo bismo preporučili knjigu [Deep Learning](https://www.amazon.com/Deep-Learning-Adaptive-Computation-Machine/dp/0262035618) autora Iana Goodfellowa, Yoshue Bengioa i Aarona Courvillea, koja je također dostupna online na [https://www.deeplearningbook.org/](https://www.deeplearningbook.org/).
Za blagi uvod u _AI u oblaku_ teme, razmislite o pohađanju [Započnite s umjetnom inteligencijom na Azure](https://docs.microsoft.com/learn/paths/get-started-with-artificial-intelligence-on-azure/?WT.mc_id=academic-77998-bethanycheum) kurikuluma.
Za blagi uvod u teme _AI u oblaku_ možete razmotriti pohađanje [Uvod u umjetnu inteligenciju na Azureu](https://docs.microsoft.com/learn/paths/get-started-with-artificial-intelligence-on-azure/?WT.mc_id=academic-77998-bethanycheum) tečaja.
# Sadržaj
| | Poveznica lekcije | PyTorch/Keras/TensorFlow | Laboratorij |
| | Poveznica na lekciju | PyTorch/Keras/TensorFlow | Laboratorij |
| :-: | :------------------------------------------------------------------------------------------------------------------------------------------: | :---------------------------------------------------------------------------------------------: | ------------------------------------------------------------------------------ |
| 0 | [Postavljanje tečaja](./lessons/0-course-setup/setup.md) | [Postavite svoje razvojno okruženje](./lessons/0-course-setup/how-to-run.md) | |
| I | [**Uvod u AI**](./lessons/1-Intro/README.md) | | |
| 01 | [Uvod i povijest AI](./lessons/1-Intro/README.md) | - | - |
| 01 | [Uvod i povijest AI-ja](./lessons/1-Intro/README.md) | - | - |
| II | **Simbolički AI** |
| 02 | [Predstavljanje znanja i ekspertni sustavi](./lessons/2-Symbolic/README.md) | [Ekspertni sustavi](https://github.com/microsoft/AI-For-Beginners/blob/main/lessons/2-Symbolic/Animals.ipynb) / [Ontologija](https://github.com/microsoft/AI-For-Beginners/blob/main/lessons/2-Symbolic/FamilyOntology.ipynb) /[Konceptualni graf](https://github.com/microsoft/AI-For-Beginners/blob/main/lessons/2-Symbolic/MSConceptGraph.ipynb) | |
| III | [**Uvod u neuralne mreže**](./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) | [Laboratorij](./lessons/3-NeuralNetworks/03-Perceptron/lab/README.md) |
| 04 | [Višeslojni perceptron i stvaranje vlastitog okvira](./lessons/3-NeuralNetworks/04-OwnFramework/README.md) | [Notebook](https://github.com/microsoft/AI-For-Beginners/blob/main/lessons/3-NeuralNetworks/04-OwnFramework/OwnFramework.ipynb) | [Laboratorij](./lessons/3-NeuralNetworks/04-OwnFramework/lab/README.md) |
| 05 | [Uvod u okvire (PyTorch/TensorFlow) i prekomjerno prilagođavanje](./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) | [Laboratorij](./lessons/3-NeuralNetworks/05-Frameworks/lab/README.md) |
| IV | [**Računalni vid**](./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)| [Istražite računalni vid na Microsoft Azure](https://learn.microsoft.com/en-us/collections/7w28iy2xrqzdj0?WT.mc_id=academic-77998-bethanycheum) |
| 06 | [Uvod u računalni vid. 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) | [Laboratorij](./lessons/4-ComputerVision/06-IntroCV/lab/README.md) |
| 07 | [Konvolucijske neuralne mreže](./lessons/4-ComputerVision/07-ConvNets/README.md) & [CNN Arhitekture](./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) | [Laboratorij](./lessons/4-ComputerVision/07-ConvNets/lab/README.md) |
| 08 | [Unaprijed trenirane mreže i prijenosno učenje](./lessons/4-ComputerVision/08-TransferLearning/README.md) i [Trikovi za treniranje](./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) | [Laboratorij](./lessons/4-ComputerVision/08-TransferLearning/lab/README.md) |
| 09 | [Autoenkoderi 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 | [Generativne suparničke mreže i prijenos umjetničkog stila](./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 | [Detekcija objekata](./lessons/4-ComputerVision/11-ObjectDetection/README.md) | [TensorFlow](https://github.com/microsoft/AI-For-Beginners/blob/main/lessons/4-ComputerVision/11-ObjectDetection/ObjectDetection.ipynb) | [Laboratorij](./lessons/4-ComputerVision/11-ObjectDetection/lab/README.md) |
| 12 | [Semantička segmentacija. 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)) | |
| 02 | [Reprezentacija znanja i ekspertni sustavi](./lessons/2-Symbolic/README.md) | [Ekspertni sustavi](./lessons/2-Symbolic/Animals.ipynb) / [Ontologija](./lessons/2-Symbolic/FamilyOntology.ipynb) /[Konceptualni graf](./lessons/2-Symbolic/MSConceptGraph.ipynb) | |
| III | [**Uvod u neuronske mreže**](./lessons/3-NeuralNetworks/README.md) |||
| 03 | [Perceptron](./lessons/3-NeuralNetworks/03-Perceptron/README.md) | [Notebook](./lessons/3-NeuralNetworks/03-Perceptron/Perceptron.ipynb) | [Laboratorij](./lessons/3-NeuralNetworks/03-Perceptron/lab/README.md) |
| 04 | [Višeslojni perceptron i stvaranje vlastitog okvira](./lessons/3-NeuralNetworks/04-OwnFramework/README.md) | [Notebook](./lessons/3-NeuralNetworks/04-OwnFramework/OwnFramework.ipynb) | [Laboratorij](./lessons/3-NeuralNetworks/04-OwnFramework/lab/README.md) |
| 05 | [Uvod u okvire (PyTorch/TensorFlow) i prenaučavanje](./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) | [Laboratorij](./lessons/3-NeuralNetworks/05-Frameworks/lab/README.md) |
| IV | [**Računalni vid**](./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)| [Istražite računalni vid na Microsoft Azureu](https://learn.microsoft.com/en-us/collections/7w28iy2xrqzdj0?WT.mc_id=academic-77998-bethanycheum) |
| 06 | [Uvod u računalni vid. OpenCV](./lessons/4-ComputerVision/06-IntroCV/README.md) | [Bilježnica](./lessons/4-ComputerVision/06-IntroCV/OpenCV.ipynb) | [Laboratorij](./lessons/4-ComputerVision/06-IntroCV/lab/README.md) |
| 07 | [Konvolucijske neuronske mreže](./lessons/4-ComputerVision/07-ConvNets/README.md) & [Arhitekture CNN-a](./lessons/4-ComputerVision/07-ConvNets/CNN_Architectures.md) | [PyTorch](./lessons/4-ComputerVision/07-ConvNets/ConvNetsPyTorch.ipynb) /[TensorFlow](./lessons/4-ComputerVision/07-ConvNets/ConvNetsTF.ipynb) | [Laboratorij](./lessons/4-ComputerVision/07-ConvNets/lab/README.md) |
| 08 | [Unaprijed istrenirane mreže i prijenosno učenje](./lessons/4-ComputerVision/08-TransferLearning/README.md) i [Trikovi za treniranje](./lessons/4-ComputerVision/08-TransferLearning/TrainingTricks.md) | [PyTorch](./lessons/4-ComputerVision/08-TransferLearning/TransferLearningPyTorch.ipynb) / [TensorFlow](./lessons/3-NeuralNetworks/05-Frameworks/IntroKerasTF.ipynb) | [Laboratorij](./lessons/4-ComputerVision/08-TransferLearning/lab/README.md) |
| 09 | [Autoenkoderi i VAE](./lessons/4-ComputerVision/09-Autoencoders/README.md) | [PyTorch](./lessons/4-ComputerVision/09-Autoencoders/AutoEncodersPyTorch.ipynb) / [TensorFlow](./lessons/4-ComputerVision/09-Autoencoders/AutoencodersTF.ipynb) | |
| 10 | [Generativne suparničke mreže i prijenos umjetničkog stila](./lessons/4-ComputerVision/10-GANs/README.md) | [PyTorch](./lessons/4-ComputerVision/10-GANs/GANPyTorch.ipynb) / [TensorFlow](./lessons/4-ComputerVision/10-GANs/GANTF.ipynb) | |
| 11 | [Detekcija objekata](./lessons/4-ComputerVision/11-ObjectDetection/README.md) | [TensorFlow](./lessons/4-ComputerVision/11-ObjectDetection/ObjectDetection.ipynb) | [Laboratorij](./lessons/4-ComputerVision/11-ObjectDetection/lab/README.md) |
| 12 | [Semantička segmentacija. 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 | [**Obrada prirodnog jezika**](./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) | [Istražite obradu prirodnog jezika na Microsoft Azureu](https://learn.microsoft.com/en-us/collections/7w28iy2xrqzdj0?WT.mc_id=academic-77998-bethanycheum)|
| 13 | [Reprezentacija teksta. 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 | [Semantičke ugrađene riječi. 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 | [Modeliranje jezika. Treniranje vlastitih ugrađenih riječi](./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) | [Laboratorij](./lessons/5-NLP/15-LanguageModeling/lab/README.md) |
| 16 | [Rekurentne neuronske mreže](./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 | [Generativne rekurentne mreže](./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) | [Laboratorij](./lessons/5-NLP/17-GenerativeNetworks/lab/README.md) |
| 18 | [Transformeri. 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 | [Prepoznavanje imenovanih entiteta](./lessons/5-NLP/19-NER/README.md) | [TensorFlow](https://microsoft.github.io/AI-For-Beginners/lessons/5-NLP/19-NER/NER-TF.ipynb) | [Laboratorij](./lessons/5-NLP/19-NER/lab/README.md) |
| 20 | [Veliki jezični modeli, programski upiti i zadaci s malo primjera](./lessons/5-NLP/20-LangModels/READMELargeLang.md) | [PyTorch](https://microsoft.github.io/AI-For-Beginners/lessons/5-NLP/20-LangModels/GPT-PyTorch.ipynb) | |
| 13 | [Reprezentacija teksta. 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 | [Semantičke ugrađenosti riječi. 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 | [Jezično modeliranje. Treniranje vlastitih ugrađenosti](./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) | [Laboratorij](./lessons/5-NLP/15-LanguageModeling/lab/README.md) |
| 16 | [Rekurentne neuronske mreže](./lessons/5-NLP/16-RNN/README.md) | [PyTorch](./lessons/5-NLP/16-RNN/RNNPyTorch.ipynb) / [TensorFlow](./lessons/5-NLP/16-RNN/RNNTF.ipynb) | |
| 17 | [Generativne rekurentne mreže](./lessons/5-NLP/17-GenerativeNetworks/README.md) | [PyTorch](./lessons/5-NLP/17-GenerativeNetworks/GenerativePyTorch.md) / [TensorFlow](./lessons/5-NLP/17-GenerativeNetworks/GenerativeTF.md) | [Laboratorij](./lessons/5-NLP/17-GenerativeNetworks/lab/README.md) |
| 18 | [Transformeri. 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 | [Prepoznavanje imenovanih entiteta](./lessons/5-NLP/19-NER/README.md) | [TensorFlow](./lessons/5-NLP/19-NER/NER-TF.ipynb) | [Laboratorij](./lessons/5-NLP/19-NER/lab/README.md) |
| 20 | [Veliki jezični modeli, programski upiti i zadaci s malo primjera](./lessons/5-NLP/20-LangModels/READMELargeLang.md) | [PyTorch](./lessons/5-NLP/20-LangModels/GPT-PyTorch.ipynb) | |
| VI | **Ostale AI tehnike** || |
| 21 | [Genetski algoritmi](./lessons/6-Other/21-GeneticAlgorithms/README.md) | [Bilježnica](https://github.com/microsoft/AI-For-Beginners/blob/main/lessons/6-Other/21-GeneticAlgorithms/Genetic.ipynb) | |
| 22 | [Duboko pojačano učenje](./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) | [Laboratorij](./lessons/6-Other/22-DeepRL/lab/README.md) |
| 21 | [Genetski algoritmi](./lessons/6-Other/21-GeneticAlgorithms/README.md) | [Bilježnica](./lessons/6-Other/21-GeneticAlgorithms/Genetic.ipynb) | |
| 22 | [Duboko pojačano učenje](./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) | [Laboratorij](./lessons/6-Other/22-DeepRL/lab/README.md) |
| 23 | [Sustavi s više agenata](./lessons/6-Other/23-MultiagentSystems/README.md) | | |
| VII | **Etika umjetne inteligencije** | | |
| 24 | [Etika umjetne inteligencije i odgovorna AI](./lessons/7-Ethics/README.md) | [Microsoft Learn: Načela odgovorne AI](https://docs.microsoft.com/learn/paths/responsible-ai-business-principles/?WT.mc_id=academic-77998-cacaste) | |
| VII | **AI etika** | | |
| 24 | [AI etika i odgovorna umjetna inteligencija](./lessons/7-Ethics/README.md) | [Microsoft Learn: Načela odgovorne AI](https://docs.microsoft.com/learn/paths/responsible-ai-business-principles/?WT.mc_id=academic-77998-cacaste) | |
| IX | **Dodaci** | | |
| 25 | [Višestruke modalne mreže, CLIP i VQGAN](./lessons/X-Extras/X1-MultiModal/README.md) | [Bilježnica](https://github.com/microsoft/AI-For-Beginners/blob/main/lessons/X-Extras/X1-MultiModal/Clip.ipynb) | |
| 25 | [Višemodalne mreže, CLIP i VQGAN](./lessons/X-Extras/X1-MultiModal/README.md) | [Bilježnica](./lessons/X-Extras/X1-MultiModal/Clip.ipynb) | |
## Svaka lekcija sadrži
* Materijale za prethodno čitanje
* Izvršne Jupyter bilježnice, koje su često specifične za okvir (**PyTorch** ili **TensorFlow**). Izvršna bilježnica također sadrži mnogo teorijskog materijala, pa je za razumijevanje teme potrebno proći barem jednu verziju bilježnice (bilo PyTorch ili TensorFlow).
* Izvršne Jupyter bilježnice, koje su često specifične za određeni okvir (**PyTorch** ili **TensorFlow**). Izvršna bilježnica također sadrži mnogo teorijskog materijala, pa je za razumijevanje teme potrebno proći barem jednu verziju bilježnice (bilo PyTorch ili TensorFlow).
* **Laboratorije** dostupne za neke teme, koji vam pružaju priliku da primijenite naučeno na specifičan problem.
* Neki odjeljci sadrže poveznice na module [**MS Learn**](https://learn.microsoft.com/en-us/collections/7w28iy2xrqzdj0?WT.mc_id=academic-77998-bethanycheum) koji pokrivaju srodne teme.
## Početak
- Kreirali smo [lekciju za postavljanje](https://github.com/microsoft/AI-For-Beginners/blob/main/lessons/0-course-setup/setup.md) kako bismo vam pomogli u postavljanju vašeg razvojnog okruženja. - Za edukatore, kreirali smo i [lekciju za postavljanje kurikuluma](https://github.com/microsoft/AI-For-Beginners/blob/main/lessons/0-course-setup/for-teachers.md)!
- Kako [pokrenuti kod u VSCode ili Codepaceu](https://github.com/microsoft/AI-For-Beginners/blob/main/lessons/0-course-setup/how-to-run.md)
- Pripremili smo [lekciju za postavljanje](./lessons/0-course-setup/setup.md) kako bismo vam pomogli u postavljanju vašeg razvojnog okruženja. - Za edukatore, pripremili smo i [lekciju za postavljanje kurikuluma](./lessons/0-course-setup/for-teachers.md)!
- Kako [pokrenuti kod u VSCode ili Codepaceu](./lessons/0-course-setup/how-to-run.md)
Slijedite ove korake:
@ -112,44 +123,46 @@ Klonirajte repozitorij: `git clone https://github.com/microsoft/AI-For-Beginners
Ne zaboravite označiti (🌟) ovaj repozitorij kako biste ga kasnije lakše pronašli.
## Upoznajte druge polaznike
## Upoznajte druge učenike
Pridružite se našem [službenom AI Discord serveru](https://aka.ms/genai-discord?WT.mc_id=academic-105485-bethanycheum) kako biste upoznali i povezali se s drugim polaznicima ovog tečaja i dobili podršku.
Pridružite se našem [službenom AI Discord serveru](https://aka.ms/genai-discord?WT.mc_id=academic-105485-bethanycheum) kako biste upoznali i povezali se s drugim učenicima koji pohađaju ovaj tečaj i dobili podršku.
Ako imate povratne informacije o proizvodu ili pitanja tijekom izrade, posjetite naš [Azure AI Foundry Developer Forum](https://aka.ms/foundry/forum)
## Kvizovi
> **Napomena o kvizovima**: Svi kvizovi nalaze se u mapi Quiz-app u etc\quiz-app. Povezani su unutar lekcija, a aplikacija za kviz može se pokrenuti lokalno ili implementirati na Azure; slijedite upute u mapi `quiz-app`. Postupno se lokaliziraju.
## Traži se pomoć
> **Napomena o kvizovima**: Svi kvizovi nalaze se u mapi Quiz-app u etc\quiz-app, ili [Online Ovdje](https://ff-quizzes.netlify.app/). Povezani su unutar lekcija, a aplikacija za kviz može se pokrenuti lokalno ili implementirati na Azure; slijedite upute u mapi `quiz-app`. Postupno se lokaliziraju.
## Potrebna Pomoć
Imate li prijedloge ili ste pronašli pravopisne ili programske pogreške? Otvorite problem ili kreirajte pull request.
## Posebne zahvale
## Posebna Zahvala
* **✍️ Glavni autor:** [Dmitry Soshnikov](http://soshnikov.com), PhD
* **🔥 Urednica:** [Jen Looper](https://twitter.com/jenlooper), PhD
* **🎨 Ilustrator sketchnote-a:** [Tomomi Imura](https://twitter.com/girlie_mac)
* **✅ Kreator kvizova:** [Lateefah Bello](https://github.com/CinnamonXI), [MLSA](https://studentambassadors.microsoft.com/)
* **🙏 Glavni suradnici:** [Evgenii Pishchik](https://github.com/Pe4enIks)
* **✍️ Glavni Autor:** [Dmitry Soshnikov](http://soshnikov.com), PhD
* **🔥 Urednica:** [Jen Looper](https://twitter.com/jenlooper), PhD
* **🎨 Ilustrator sketchnoteova:** [Tomomi Imura](https://twitter.com/girlie_mac)
* **✅ Kreator kvizova:** [Lateefah Bello](https://github.com/CinnamonXI), [MLSA](https://studentambassadors.microsoft.com/)
* **🙏 Glavni Suradnici:** [Evgenii Pishchik](https://github.com/Pe4enIks)
## Ostali kurikulumi
## Ostali Kurikulumi
Naš tim proizvodi i druge kurikulume! Pogledajte:
Naš tim izrađuje i druge kurikulume! Pogledajte:
- [Generativna AI za početnike](https://aka.ms/genai-beginners)
- [Generativna AI za početnike .NET](https://github.com/microsoft/Generative-AI-for-beginners-dotnet)
- [Generativna AI s JavaScriptom](https://github.com/microsoft/generative-ai-with-javascript)
- [Generativna AI s Javom](https://github.com/microsoft/Generative-AI-for-beginners-java)
- [AI za početnike](https://aka.ms/ai-beginners)
- [Data Science za početnike](https://aka.ms/datascience-beginners)
- [ML za početnike](https://aka.ms/ml-beginners)
- [Kibernetička sigurnost za početnike](https://github.com/microsoft/Security-101)
- [Web razvoj za početnike](https://aka.ms/webdev-beginners)
- [IoT za početnike](https://aka.ms/iot-beginners)
- [XR razvoj za početnike](https://github.com/microsoft/xr-development-for-beginners)
- [Ovladavanje GitHub Copilotom za agentičku upotrebu](https://github.com/microsoft/Mastering-GitHub-Copilot-for-Paired-Programming)
- [Ovladavanje GitHub Copilotom za C#/.NET programere](https://github.com/microsoft/mastering-github-copilot-for-dotnet-csharp-developers)
- [Odaberi svoju Copilot avanturu](https://github.com/microsoft/CopilotAdventures)
- [Generativna AI za Početnike](https://aka.ms/genai-beginners)
- [Generativna AI za Početnike .NET](https://github.com/microsoft/Generative-AI-for-beginners-dotnet)
- [Generativna AI s JavaScriptom](https://github.com/microsoft/generative-ai-with-javascript)
- [Generativna AI s Javom](https://github.com/microsoft/Generative-AI-for-beginners-java)
- [AI za Početnike](https://aka.ms/ai-beginners)
- [Data Science za Početnike](https://aka.ms/datascience-beginners)
- [Strojno Učenje za Početnike](https://aka.ms/ml-beginners)
- [Kibernetička Sigurnost za Početnike](https://github.com/microsoft/Security-101)
- [Web Razvoj za Početnike](https://aka.ms/webdev-beginners)
- [IoT za Početnike](https://aka.ms/iot-beginners)
- [XR Razvoj za Početnike](https://github.com/microsoft/xr-development-for-beginners)
- [Ovladavanje GitHub Copilotom za Agentnu Upotrebu](https://github.com/microsoft/Mastering-GitHub-Copilot-for-Paired-Programming)
- [Ovladavanje GitHub Copilotom za C#/.NET Razvijače](https://github.com/microsoft/mastering-github-copilot-for-dotnet-csharp-developers)
- [Odaberite Svoju Vlastitu Copilot Avanturu](https://github.com/microsoft/CopilotAdventures)
---
**Odricanje od odgovornosti**:
Ovaj dokument je preveden pomoću AI usluge za prevođenje [Co-op Translator](https://github.com/Azure/co-op-translator). Iako nastojimo osigurati točnost, imajte na umu da automatski prijevodi mogu sadržavati pogreške ili netočnosti. Izvorni dokument na izvornom jeziku treba smatrati mjerodavnim izvorom. Za ključne informacije preporučuje se profesionalni prijevod od strane stručnjaka. Ne preuzimamo odgovornost za bilo kakve nesporazume ili pogrešne interpretacije proizašle iz korištenja ovog prijevoda.
Ovaj dokument je preveden korištenjem AI usluge za prevođenje [Co-op Translator](https://github.com/Azure/co-op-translator). Iako nastojimo osigurati točnost, imajte na umu da automatski prijevodi mogu sadržavati pogreške ili netočnosti. Izvorni dokument na izvornom jeziku treba smatrati mjerodavnim izvorom. Za ključne informacije preporučuje se profesionalni prijevod od strane stručnjaka. Ne preuzimamo odgovornost za bilo kakva nesporazuma ili pogrešna tumačenja koja mogu proizaći iz korištenja ovog prijevoda.

View File

@ -0,0 +1,478 @@
{
"cells": [
{
"cell_type": "markdown",
"metadata": {
"collapsed": true
},
"source": [
"# Implementacija stručnog sustava za životinje\n",
"\n",
"Primjer iz [AI for Beginners Curriculum](http://github.com/microsoft/ai-for-beginners).\n",
"\n",
"U ovom primjeru implementirat ćemo jednostavan sustav temeljen na znanju za određivanje životinje na temelju nekih fizičkih karakteristika. Sustav se može prikazati sljedećim AND-OR stablom (ovo je dio cijelog stabla, lako možemo dodati još pravila):\n",
"\n",
"![](../../../../translated_images/AND-OR-Tree.5592d2c70187f283703c8e9c0d69d6a786eb370f4ace67f9a7aae5ada3d260b0.hr.png)\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Naša vlastita ljuska za ekspertne sustave s unatrag zaključivanjem\n",
"\n",
"Pokušajmo definirati jednostavan jezik za reprezentaciju znanja temeljen na produkcijskim pravilima. Koristit ćemo Python klase kao ključne riječi za definiranje pravila. Postojat će tri osnovne vrste klasa:\n",
"* `Ask` predstavlja pitanje koje treba postaviti korisniku. Sadrži skup mogućih odgovora.\n",
"* `If` predstavlja pravilo i samo je sintaktički šećer za pohranu sadržaja pravila.\n",
"* `AND`/`OR` su klase za predstavljanje AND/OR grana stabla. One samo pohranjuju popis argumenata unutar sebe. Kako bismo pojednostavili kod, sva funkcionalnost je definirana u roditeljskoj klasi `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": [
"U našem sustavu, radna memorija bi sadržavala popis **činjenica** kao **parova atribut-vrijednost**. Baza znanja može se definirati kao jedan veliki rječnik koji povezuje radnje (nove činjenice koje bi trebale biti umetnute u radnu memoriju) s uvjetima, izraženima kao AND-OR izrazi. Također, neke činjenice mogu biti `Pitaj`-ane.\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": [
"Za izvođenje unatrag zaključivanja, definirat ćemo klasu `Knowledgebase`. Ona će sadržavati:\n",
"* Radnu `memoriju` - rječnik koji mapira atribute na vrijednosti\n",
"* Pravila `Knowledgebase` u formatu definiranom gore\n",
"\n",
"Dvije glavne metode su:\n",
"* `get` za dobivanje vrijednosti atributa, uz izvođenje zaključivanja ako je potrebno. Na primjer, `get('color')` bi dobio vrijednost za atribut boje (pitati će ako je potrebno i pohraniti vrijednost za kasniju upotrebu u radnoj memoriji). Ako pitamo `get('color:blue')`, tražit će boju, a zatim vratiti vrijednost `y`/`n` ovisno o boji.\n",
"* `eval` izvodi stvarno zaključivanje, tj. prolazi kroz AND/OR stablo, procjenjuje podciljeve itd.\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": [
"Sada definirajmo našu bazu znanja o životinjama i obavimo konzultaciju. Imajte na umu da će vas ovaj poziv postavljati pitanja. Možete odgovoriti upisivanjem `y`/`n` za pitanja s odgovorima da-ne, ili navođenjem broja (0..N) za pitanja s duljim odgovorima s više izbora.\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": [
"## Korištenje PyKnow za naprijedno zaključivanje\n",
"\n",
"U sljedećem primjeru pokušat ćemo implementirati naprijedno zaključivanje koristeći jednu od biblioteka za reprezentaciju znanja, [PyKnow](https://github.com/buguroo/pyknow/). **PyKnow** je biblioteka za kreiranje sustava naprijednog zaključivanja u Pythonu, koja je dizajnirana da bude slična klasičnom starom sustavu [CLIPS](http://www.clipsrules.net/index.html).\n",
"\n",
"Također smo mogli sami implementirati naprijedno povezivanje bez većih problema, ali naivne implementacije obično nisu vrlo učinkovite. Za učinkovitije podudaranje pravila koristi se poseban algoritam [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": [
"Definirat ćemo naš sustav kao klasu koja nasljeđuje `KnowledgeEngine`. Svako pravilo definirano je zasebnom funkcijom s anotacijom `@Rule`, koja specificira kada bi se pravilo trebalo aktivirati. Unutar pravila možemo dodavati nove činjenice koristeći funkciju `declare`, a dodavanje tih činjenica rezultirat će pozivanjem dodatnih pravila od strane stroja za zaključivanje unaprijed.\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": [
"Nakon što definiramo bazu znanja, popunjavamo radnu memoriju s nekim početnim činjenicama, a zatim pozivamo metodu `run()` za izvođenje zaključivanja. Kao rezultat možete vidjeti da se nove izvedene činjenice dodaju u radnu memoriju, uključujući konačnu činjenicu o životinji (ako smo ispravno postavili sve početne činjenice).\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**Odricanje od odgovornosti**: \nOvaj dokument je preveden korištenjem AI usluge za prevođenje [Co-op Translator](https://github.com/Azure/co-op-translator). Iako nastojimo osigurati točnost, imajte na umu da automatski prijevodi mogu sadržavati pogreške ili netočnosti. Izvorni dokument na izvornom jeziku treba smatrati mjerodavnim izvorom. Za ključne informacije preporučuje se profesionalni prijevod od strane stručnjaka. Ne preuzimamo odgovornost za bilo kakva nesporazuma ili pogrešna tumačenja koja mogu proizaći iz korištenja ovog prijevoda.\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-30T07:41:52+00:00",
"source_file": "lessons/2-Symbolic/Animals.ipynb",
"language_code": "hr"
}
},
"nbformat": 4,
"nbformat_minor": 2
}

View File

@ -0,0 +1,595 @@
{
"cells": [
{
"cell_type": "markdown",
"metadata": {
"collapsed": true
},
"source": [
"# Ontologija obiteljskih odnosa\n",
"\n",
"Ovaj primjer dio je [kurikuluma AI za početnike](http://github.com/microsoft/ai-for-beginners), a inspiriran je [ovim blog postom](https://habr.com/post/270857/).\n",
"\n",
"Uvijek mi je teško zapamtiti različite odnose među ljudima u obitelji. U ovom primjeru, uzet ćemo ontologiju koja definira obiteljske odnose i stvarno rodoslovno stablo te ćemo pokazati kako možemo automatski izvesti sve rodbinske veze.\n",
"\n",
"### Dobivanje rodoslovnog stabla\n",
"\n",
"Kao primjer, uzet ćemo rodoslovno stablo [carske obitelji Romanov](https://en.wikipedia.org/wiki/House_of_Romanov). Najčešći format za opisivanje obiteljskih odnosa je [GEDCOM](https://en.wikipedia.org/wiki/GEDCOM). Uzet ćemo rodoslovno stablo obitelji Romanov u GEDCOM formatu:\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": [
"Za korištenje GEDCOM datoteke, možemo koristiti biblioteku `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": [
"Ova biblioteka uklanja neke tehničke probleme s parsiranjem datoteka, ali nam i dalje omogućuje prilično niskorazinski pristup svim pojedincima i obiteljima u stablu. Evo kako možemo parsirati datoteku i prikazati popis svih pojedinaca:\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": [
"Evo kako možemo dobiti informacije o obiteljima. Napominjemo da nam daje popis **identifikatora**, i trebamo ih pretvoriti u imena ako želimo više jasnoće:\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": [
"### Dobivanje obiteljske ontologije\n",
"\n",
"Zatim, pogledajmo [obiteljsku ontologiju](https://raw.githubusercontent.com/blokhin/genealogical-trees/master/data/header.ttl) definiranu kao skup trojki Semantičkog weba. Ova ontologija definira odnose poput `isUncleOf`, `isCousinOf` i mnoge druge. Svi ti odnosi definirani su pomoću osnovnih predikata `isMotherOf`, `isFatherOf`, `isBrotherOf` i `isSisterOf`. Koristit ćemo automatsko zaključivanje kako bismo iz ontologije izveli sve ostale odnose.\n",
"\n",
"Evo primjera definicije svojstva `isAuntOf`, koje je definirano kao kompozicija `isSisterOf` i `isParentOf` (*Teta je sestra nečijeg roditelja*).\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": [
"### Izrada ontologije za zaključivanje\n",
"\n",
"Radi jednostavnosti, izradit ćemo jednu ontološku datoteku koja će uključivati izvorna pravila iz obiteljske ontologije i činjenice o pojedincima iz naše GEDCOM datoteke. Proći ćemo kroz GEDCOM datoteku, izvući informacije o obiteljima i pojedincima te ih pretvoriti u trojke.\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": [
"### Provođenje zaključivanja\n",
"\n",
"Sada želimo koristiti ovu ontologiju za zaključivanje i upite. Koristit ćemo [RDFLib](https://github.com/RDFLib), biblioteku za čitanje RDF grafova u različitim formatima, postavljanje upita itd.\n",
"\n",
"Za logičko zaključivanje koristit ćemo biblioteku [OWL-RL](https://github.com/RDFLib/OWL-RL), koja nam omogućuje izgradnju **Closure** RDF grafa, tj. dodavanje svih mogućih koncepata i odnosa koji se mogu zaključiti.\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": [
"Otvorimo ontološku datoteku i vidimo koliko trojki sadrži:\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": [
"Sada izgradimo zatvaranje i vidimo kako se broj trojki povećava:\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": [
"### Upit za rodbinske veze\n",
"\n",
"Sada možemo ispitivati graf kako bismo vidjeli različite odnose među ljudima. Možemo koristiti jezik **SPARQL** zajedno s metodom `query`. U našem slučaju, pogledajmo sve **ujake** u našem obiteljskom stablu:\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": [
"Slobodno eksperimentirajte s različitim drugim obiteljskim odnosima. Na primjer, možete pogledati odnos `isAncestorOf`, koji rekurzivno definira sve pretke određene osobe.\n",
"\n",
"Na kraju, pospremimo!\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**Odricanje od odgovornosti**: \nOvaj dokument je preveden pomoću AI usluge za prevođenje [Co-op Translator](https://github.com/Azure/co-op-translator). Iako nastojimo osigurati točnost, imajte na umu da automatski prijevodi mogu sadržavati pogreške ili netočnosti. Izvorni dokument na izvornom jeziku treba smatrati autoritativnim izvorom. Za ključne informacije preporučuje se profesionalni prijevod od strane čovjeka. Ne preuzimamo odgovornost za bilo kakva nesporazuma ili pogrešna tumačenja koja proizlaze iz korištenja ovog prijevoda.\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-30T07:40:28+00:00",
"source_file": "lessons/2-Symbolic/FamilyOntology.ipynb",
"language_code": "hr"
}
},
"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/) je velika taksonomija pojmova dobivena s interneta, s `is-a` odnosima između koncepata.\n",
"\n",
"Context Graph dostupan je u dva oblika:\n",
" * Velika tekstualna datoteka za preuzimanje\n",
" * REST API\n",
"\n",
"Statistika:\n",
" * 5401933 jedinstvenih koncepata,\n",
" * 12551613 jedinstvenih instanci\n",
" * 87603947 `is-a` odnosa\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Korištenje web servisa\n",
"\n",
"Web servis nudi različite pozive za procjenu vjerojatnosti da koncept pripada različitim grupama. Više informacija dostupno je [ovdje](https://concept.research.microsoft.com/Home/Api). \n",
"Evo primjera URL-a za poziv: `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": [
"Pokušajmo kategorizirati naslove vijesti koristeći roditeljske pojmove. Za dobivanje naslova vijesti koristit ćemo uslugu [NewsApi.org](http://newsapi.org). Morate dobiti vlastiti API ključ kako biste koristili uslugu - posjetite web stranicu i registrirajte se za besplatni plan za programere.\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": [
"Prije svega, želimo biti u mogućnosti izdvojiti imenice iz naslova vijesti. Koristit ćemo biblioteku `TextBlob` za to, koja pojednostavljuje mnoge tipične NLP zadatke poput ovog.\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": [
"Možemo vidjeti da imenice ne daju velike tematske skupine. Zamijenimo imenice općenitijim pojmovima dobivenim iz konceptualnog grafa. Ovo će potrajati neko vrijeme, jer radimo REST poziv za svaku imeničnu frazu.\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**Odricanje od odgovornosti**: \nOvaj dokument je preveden korištenjem AI usluge za prevođenje [Co-op Translator](https://github.com/Azure/co-op-translator). Iako nastojimo osigurati točnost, imajte na umu da automatski prijevodi mogu sadržavati pogreške ili netočnosti. Izvorni dokument na izvornom jeziku treba smatrati mjerodavnim izvorom. Za ključne informacije preporučuje se profesionalni prijevod od strane stručnjaka. Ne preuzimamo odgovornost za bilo kakva nesporazuma ili pogrešna tumačenja koja mogu proizaći iz korištenja ovog prijevoda.\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-30T07:41:13+00:00",
"source_file": "lessons/2-Symbolic/MSConceptGraph.ipynb",
"language_code": "hr"
}
},
"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:01:13+00:00",
"original_hash": "ba5d1eb353d20d3e7181066b3c424b99",
"translation_date": "2025-08-30T07:06:15+00:00",
"source_file": "lessons/3-NeuralNetworks/03-Perceptron/lab/README.md",
"language_code": "hr"
}
@ -13,19 +13,21 @@ Laboratorijska vježba iz [AI for Beginners Curriculum](https://github.com/micro
## Zadatak
Koristeći kod koji smo razvili u ovoj lekciji za binarnu klasifikaciju MNIST rukom pisanih znamenki, kreirajte višeklasni klasifikator koji će moći prepoznati bilo koju znamenku. Izračunajte točnost klasifikacije na skupu za treniranje i testiranje te ispišite matricu konfuzije.
Koristeći kod koji smo razvili u ovoj lekciji za binarnu klasifikaciju rukom pisanih znamenki iz MNIST skupa podataka, kreirajte višeklasni klasifikator koji će moći prepoznati bilo koju znamenku. Izračunajte točnost klasifikacije na skupu za treniranje i testiranje te ispišite matricu zabune.
## Savjeti
1. Za svaku znamenku kreirajte skup podataka za binarnu klasifikaciju "ova znamenka naspram svih ostalih znamenki"
1. Trenirajte 10 različitih perceptrona za binarnu klasifikaciju (jedan za svaku znamenku)
1. Trenirajte 10 različitih perceptrona za binarnu klasifikaciju (po jedan za svaku znamenku)
1. Definirajte funkciju koja će klasificirati ulaznu znamenku
> **Savjet**: Ako kombiniramo težine svih 10 perceptrona u jednu matricu, trebali bismo moći primijeniti svih 10 perceptrona na ulazne znamenke jednom matričnom množenjem. Najvjerojatnija znamenka može se zatim pronaći jednostavno primjenom `argmax` operacije na izlaz.
> **Savjet**: Ako kombiniramo težine svih 10 perceptrona u jednu matricu, trebali bismo moći primijeniti svih 10 perceptrona na ulazne znamenke pomoću jedne matrične množenja. Najvjerojatnija znamenka može se zatim pronaći jednostavnim primjenom `argmax` operacije na izlazu.
## Početna bilježnica
Započnite laboratorijsku vježbu otvaranjem [PerceptronMultiClass.ipynb](../../../../../../lessons/3-NeuralNetworks/03-Perceptron/lab/PerceptronMultiClass.ipynb)
Započnite laboratorijsku vježbu otvaranjem [PerceptronMultiClass.ipynb](PerceptronMultiClass.ipynb)
---
**Odricanje od odgovornosti**:
Ovaj dokument je preveden pomoću AI usluge za prevođenje [Co-op Translator](https://github.com/Azure/co-op-translator). Iako nastojimo osigurati točnost, imajte na umu da automatski prijevodi mogu sadržavati pogreške ili netočnosti. Izvorni dokument na izvornom jeziku treba smatrati autoritativnim izvorom. Za kritične informacije preporučuje se profesionalni prijevod od strane čovjeka. Ne preuzimamo odgovornost za nesporazume ili pogrešna tumačenja koja mogu proizaći iz korištenja ovog prijevoda.
Ovaj dokument je preveden korištenjem AI usluge za prevođenje [Co-op Translator](https://github.com/Azure/co-op-translator). Iako nastojimo osigurati točnost, imajte na umu da automatski prijevodi mogu sadržavati pogreške ili netočnosti. Izvorni dokument na izvornom jeziku treba smatrati mjerodavnim izvorom. Za ključne informacije preporučuje se profesionalni prijevod od strane stručnjaka. Ne preuzimamo odgovornost za bilo kakva nesporazuma ili pogrešna tumačenja koja mogu proizaći iz korištenja ovog prijevoda.

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,183 @@
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Klasifikacija MNIST znamenki s našim vlastitim okvirom\n",
"\n",
"Laboratorijska vježba iz [AI za početnike kurikuluma](https://github.com/microsoft/ai-for-beginners).\n",
"\n",
"### Čitanje skupa podataka\n",
"\n",
"Ovaj kod preuzima skup podataka iz spremišta na internetu. Također možete ručno kopirati skup podataka iz direktorija `/data` u repozitoriju AI kurikuluma.\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": [
"Pogledajmo kakav je oblik podataka koje imamo:\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": [
"### Razdvajanje podataka\n",
"\n",
"Koristit ćemo Scikit Learn za razdvajanje podataka na skup za treniranje i skup za testiranje:\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": [
"### Upute\n",
"\n",
"1. Uzmite okvirni kod iz lekcije i zalijepite ga u ovu bilježnicu ili (još bolje) u zaseban Python modul\n",
"1. Definirajte i trenirajte perceptron s jednim slojem, promatrajući točnost na skupu za treniranje i validaciju tijekom treninga\n",
"1. Pokušajte razumjeti je li došlo do prenaučenosti i prilagodite parametre sloja kako biste poboljšali točnost\n",
"1. Ponovite prethodne korake za perceptrone s 2 i 3 sloja. Pokušajte eksperimentirati s različitim aktivacijskim funkcijama između slojeva.\n",
"1. Pokušajte odgovoriti na sljedeća pitanja:\n",
" - Utječe li aktivacijska funkcija između slojeva na performanse mreže?\n",
" - Treba li nam mreža s 2 ili 3 sloja za ovaj zadatak?\n",
" - Jeste li naišli na probleme tijekom treniranja mreže? Pogotovo kako se broj slojeva povećavao.\n",
" - Kako se težine mreže ponašaju tijekom treniranja? Možete nacrtati graf maksimalne apsolutne vrijednosti težina u odnosu na epohe kako biste razumjeli odnos.\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": []
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"\n---\n\n**Odricanje od odgovornosti**: \nOvaj dokument je preveden korištenjem AI usluge za prevođenje [Co-op Translator](https://github.com/Azure/co-op-translator). Iako nastojimo osigurati točnost, imajte na umu da automatski prijevodi mogu sadržavati pogreške ili netočnosti. Izvorni dokument na izvornom jeziku treba smatrati mjerodavnim izvorom. Za ključne informacije preporučuje se profesionalni prijevod od strane stručnjaka. Ne preuzimamo odgovornost za bilo kakve nesporazume ili pogrešne interpretacije proizašle iz korištenja ovog prijevoda.\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-30T07:45:33+00:00",
"source_file": "lessons/3-NeuralNetworks/04-OwnFramework/lab/MyFW_MNIST.ipynb",
"language_code": "hr"
}
},
"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": [
"## Otkrivanje pokreta dlana pomoću optičkog toka\n",
"\n",
"Ova radionica dio je [kurikuluma AI za početnike](http://aka.ms/ai-beginners).\n",
"\n",
"Razmotrite [ovaj video](../../../../../../lessons/4-ComputerVision/06-IntroCV/lab/palm-movement.mp4), u kojem se dlan osobe pomiče lijevo/desno/gore/dolje na stabilnoj pozadini.\n",
"\n",
"**Vaš cilj** bio bi koristiti optički tok kako biste odredili koji dijelovi videa sadrže pokrete gore/dolje/lijevo/desno.\n",
"\n",
"Započnite s dobivanjem okvira iz videa kako je opisano na predavanju:\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# Code here"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Sada izračunajte gusto optičko strujanje okvira kako je opisano na predavanju i pretvorite gusto optičko strujanje u polarne koordinate:\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# Code here"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Izgradite histogram smjerova za svaki optički tok okvira. Histogram prikazuje koliko vektora spada u određeni bin i trebao bi razdvojiti različite smjerove kretanja na okviru.\n",
"\n",
"> Možda ćete također htjeti postaviti na nulu sve vektore čija je magnituda ispod određenog praga. Ovo će ukloniti male dodatne pokrete u videu, poput pokreta očiju i glave.\n",
"\n",
"Prikažite histograme za neke od okvira.\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# Code here"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Gledajući histograme, trebalo bi biti prilično jednostavno odrediti smjer kretanja. Trebate odabrati one stupce koji odgovaraju smjerovima gore/dolje/lijevo/desno i koji su iznad određenog praga.\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# Code here"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Čestitamo! Ako ste izvršili sve gore navedene korake, završili ste laboratorij!\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"\n---\n\n**Odricanje od odgovornosti**: \nOvaj dokument je preveden pomoću AI usluge za prevođenje [Co-op Translator](https://github.com/Azure/co-op-translator). Iako nastojimo osigurati točnost, imajte na umu da automatski prijevodi mogu sadržavati pogreške ili netočnosti. Izvorni dokument na izvornom jeziku treba smatrati autoritativnim izvorom. Za ključne informacije preporučuje se profesionalni prijevod od strane ljudskog prevoditelja. Ne preuzimamo odgovornost za bilo kakve nesporazume ili pogrešne interpretacije koje proizlaze iz korištenja ovog prijevoda.\n"
]
}
],
"metadata": {
"language_info": {
"name": "python"
},
"orig_nbformat": 4,
"coopTranslator": {
"original_hash": "153d9e417e079bf62f8f693002d0deaf",
"translation_date": "2025-08-30T07:29:15+00:00",
"source_file": "lessons/4-ComputerVision/06-IntroCV/lab/MovementDetection.ipynb",
"language_code": "hr"
}
},
"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": [
"# Zadatak klasifikacije teksta\n",
"\n",
"Kao što smo spomenuli, fokusirat ćemo se na jednostavan zadatak klasifikacije teksta temeljen na **AG_NEWS** skupu podataka, gdje je cilj klasificirati naslove vijesti u jednu od 4 kategorije: Svijet, Sport, Poslovanje i Znanost/Tehnologija.\n",
"\n",
"## Skup podataka\n",
"\n",
"Ovaj skup podataka ugrađen je u modul [`torchtext`](https://github.com/pytorch/text), tako da mu možemo lako pristupiti.\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": [
"Ovdje, `train_dataset` i `test_dataset` sadrže kolekcije koje vraćaju parove oznake (broj klase) i teksta, na primjer:\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": [
"Dakle, ispisat ćemo prvih 10 novih naslova iz našeg skupa podataka:\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": [
"Budući da su skupovi podataka iteratori, ako želimo koristiti podatke više puta, moramo ih pretvoriti u popis:\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": [
"## Tokenizacija\n",
"\n",
"Sada trebamo pretvoriti tekst u **brojeve** koji se mogu predstaviti kao tenzori. Ako želimo reprezentaciju na razini riječi, trebamo učiniti dvije stvari:\n",
"* koristiti **tokenizator** za razdvajanje teksta na **tokene**\n",
"* izgraditi **vokabular** tih tokena.\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": [
"Korištenjem rječnika, možemo lako kodirati naš tokenizirani niz u skup brojeva:\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": [
"## Predstavljanje teksta pomoću vreće riječi\n",
"\n",
"Budući da riječi nose značenje, ponekad možemo shvatiti značenje teksta samo promatrajući pojedinačne riječi, bez obzira na njihov redoslijed u rečenici. Na primjer, prilikom klasifikacije vijesti, riječi poput *vrijeme*, *snijeg* vjerojatno ukazuju na *vremensku prognozu*, dok bi riječi poput *dionice*, *dolar* upućivale na *financijske vijesti*.\n",
"\n",
"**Vreća riječi** (BoW) vektorsko predstavljanje najčešće je korišteno tradicionalno vektorsko predstavljanje. Svaka riječ povezana je s indeksom vektora, a element vektora sadrži broj pojavljivanja riječi u određenom dokumentu.\n",
"\n",
"![Slika koja prikazuje kako je vektorsko predstavljanje vreće riječi prikazano u memoriji.](../../../../../translated_images/bag-of-words-example.606fc1738f1d7ba98a9d693e3bcd706c6e83fa7bf8221e6e90d1a206d82f2ea4.hr.png) \n",
"\n",
"> **Napomena**: Možete također razmišljati o BoW kao o zbroju svih one-hot kodiranih vektora za pojedinačne riječi u tekstu.\n",
"\n",
"Ispod je primjer kako generirati predstavljanje vreće riječi koristeći Scikit Learn python biblioteku:\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": [
"Za izračunavanje vektora vreće riječi iz vektorske reprezentacije našeg AG_NEWS skupa podataka, možemo koristiti sljedeću funkciju:\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": [
"> **Napomena:** Ovdje koristimo globalnu varijablu `vocab_size` za određivanje zadane veličine rječnika. Budući da je veličina rječnika često prilično velika, možemo ograničiti veličinu rječnika na najčešće riječi. Pokušajte smanjiti vrijednost `vocab_size` i pokrenuti donji kod te vidjeti kako to utječe na točnost. Možete očekivati određeni pad točnosti, ali ne dramatičan, u zamjenu za bolje performanse.\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Treniranje BoW klasifikatora\n",
"\n",
"Sada kada smo naučili kako izgraditi Bag-of-Words reprezentaciju našeg teksta, trenirajmo klasifikator na temelju nje. Prvo, trebamo prilagoditi naš skup podataka za treniranje na način da se sve pozicijske vektorske reprezentacije pretvore u Bag-of-Words reprezentaciju. To se može postići prosljeđivanjem funkcije `bowify` kao parametra `collate_fn` standardnom 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": [
"Sada definirajmo jednostavnu neuronsku mrežu klasifikatora koja sadrži jedan linearni sloj. Veličina ulaznog vektora jednaka je `vocab_size`, a izlazna veličina odgovara broju klasa (4). Budući da rješavamo zadatak klasifikacije, konačna aktivacijska funkcija je `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": [
"Sada ćemo definirati standardnu PyTorch petlju za treniranje. Budući da je naš skup podataka prilično velik, za potrebe podučavanja trenirat ćemo samo jedan epoch, a ponekad čak i manje od jednog epocha (navođenjem parametra `epoch_size` možemo ograničiti treniranje). Također ćemo izvještavati o akumuliranoj točnosti treniranja tijekom treniranja; učestalost izvještavanja određuje se pomoću parametra `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": [
"## BiGrami, TriGrami i N-Grami\n",
"\n",
"Jedno ograničenje pristupa vreće riječi je da su neke riječi dio višerječnih izraza, na primjer, riječ 'hot dog' ima potpuno drugačije značenje od riječi 'hot' i 'dog' u drugim kontekstima. Ako uvijek predstavljamo riječi 'hot' i 'dog' istim vektorima, to može zbuniti naš model.\n",
"\n",
"Kako bismo to riješili, **N-gram reprezentacije** često se koriste u metodama klasifikacije dokumenata, gdje je učestalost svake riječi, dvostruke riječi ili trostruke riječi korisna značajka za treniranje klasifikatora. U bigram reprezentaciji, na primjer, dodajemo sve parove riječi u vokabular, uz originalne riječi.\n",
"\n",
"Ispod je primjer kako generirati bigram reprezentaciju vreće riječi koristeći 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": [
"Glavni nedostatak N-gram pristupa je što veličina rječnika počinje rasti izuzetno brzo. U praksi, potrebno je kombinirati N-gram reprezentaciju s nekim tehnikama smanjenja dimenzionalnosti, poput *ugrađivanja* (embeddings), o čemu ćemo raspravljati u sljedećoj jedinici.\n",
"\n",
"Kako bismo koristili N-gram reprezentaciju u našem **AG News** skupu podataka, trebamo izgraditi poseban ngram rječnik:\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": [
"Mogli bismo koristiti isti kod kao gore za treniranje klasifikatora, no to bi bilo vrlo neučinkovito u pogledu memorije. U sljedećoj jedinici, trenirat ćemo bigram klasifikator koristeći ugradnje.\n",
"\n",
"> **Napomena:** Možete zadržati samo one n-grame koji se pojavljuju u tekstu više od određenog broja puta. Ovo će osigurati da se rijetki bigrami izostave i značajno smanjiti dimenzionalnost. Da biste to postigli, postavite parametar `min_freq` na višu vrijednost i promatrajte kako se duljina rječnika mijenja.\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Frekvencija termina i inverzna frekvencija dokumenata (TF-IDF)\n",
"\n",
"U BoW (Bag of Words) reprezentaciji, pojavljivanja riječi imaju jednaku težinu, bez obzira na samu riječ. Međutim, jasno je da su učestale riječi, poput *a*, *u*, itd., mnogo manje važne za klasifikaciju od specijaliziranih pojmova. Zapravo, u većini NLP zadataka neke su riječi relevantnije od drugih.\n",
"\n",
"**TF-IDF** označava **frekvenciju termina inverznu frekvenciju dokumenata**. To je varijacija metode vreće riječi, gdje se umjesto binarne vrijednosti 0/1, koja označava pojavljivanje riječi u dokumentu, koristi vrijednost s pomičnim zarezom, koja je povezana s učestalošću pojavljivanja riječi u korpusu.\n",
"\n",
"Formalnije, težina $w_{ij}$ riječi $i$ u dokumentu $j$ definirana je kao:\n",
"$$\n",
"w_{ij} = tf_{ij}\\times\\log({N\\over df_i})\n",
"$$\n",
"gdje su:\n",
"* $tf_{ij}$ broj pojavljivanja riječi $i$ u dokumentu $j$, tj. BoW vrijednost koju smo ranije vidjeli\n",
"* $N$ broj dokumenata u zbirci\n",
"* $df_i$ broj dokumenata koji sadrže riječ $i$ u cijeloj zbirci\n",
"\n",
"TF-IDF vrijednost $w_{ij}$ raste proporcionalno broju pojavljivanja riječi u dokumentu, ali se smanjuje u odnosu na broj dokumenata u korpusu koji sadrže tu riječ, što pomaže u prilagodbi činjenici da se neke riječi pojavljuju češće od drugih. Na primjer, ako se riječ pojavljuje u *svakom* dokumentu u zbirci, tada je $df_i=N$, i $w_{ij}=0$, te bi ti termini bili potpuno zanemareni.\n",
"\n",
"TF-IDF vektorizaciju teksta možete jednostavno kreirati pomoću 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": [
"## Zaključak\n",
"\n",
"Iako TF-IDF reprezentacije dodjeljuju težinu učestalosti različitim riječima, one nisu sposobne prikazati značenje ili redoslijed. Kao što je poznati lingvist J. R. Firth rekao 1935. godine: \"Potpuno značenje riječi uvijek je kontekstualno, i nijedno proučavanje značenja izvan konteksta ne može se smatrati ozbiljnim.\" Kasnije u ovom tečaju naučit ćemo kako izvući kontekstualne informacije iz teksta koristeći jezično modeliranje.\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"\n---\n\n**Odricanje od odgovornosti**: \nOvaj dokument je preveden pomoću AI usluge za prevođenje [Co-op Translator](https://github.com/Azure/co-op-translator). Iako nastojimo osigurati točnost, imajte na umu da automatski prijevodi mogu sadržavati pogreške ili netočnosti. Izvorni dokument na izvornom jeziku treba smatrati mjerodavnim izvorom. Za ključne informacije preporučuje se profesionalni prijevod od strane stručnjaka. Ne preuzimamo odgovornost za bilo kakve nesporazume ili pogrešne interpretacije proizašle iz korištenja ovog prijevoda.\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-30T08:17:08+00:00",
"source_file": "lessons/5-NLP/13-TextRep/TextRepresentationPyTorch.ipynb",
"language_code": "hr"
}
},
"nbformat": 4,
"nbformat_minor": 2
}

View File

@ -0,0 +1,647 @@
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Zadatak klasifikacije teksta\n",
"\n",
"U ovom modulu započet ćemo s jednostavnim zadatkom klasifikacije teksta temeljenim na skupu podataka **[AG_NEWS](http://www.di.unipi.it/~gulli/AG_corpus_of_news_articles.html)**: klasificirat ćemo naslove vijesti u jednu od 4 kategorije: Svijet, Sport, Poslovanje i Znanost/Tehnologija.\n",
"\n",
"## Skup podataka\n",
"\n",
"Za učitavanje skupa podataka koristit ćemo API **[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": [
"Sada možemo pristupiti treninzima i testnim dijelovima skupa podataka koristeći `dataset['train']` i `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": [
"Ispišimo prvih 10 novih naslova iz našeg skupa podataka:\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": [
"## Vektorizacija teksta\n",
"\n",
"Sada trebamo pretvoriti tekst u **brojeve** koji se mogu predstaviti kao tenzori. Ako želimo reprezentaciju na razini riječi, trebamo napraviti dvije stvari:\n",
"\n",
"* Koristiti **tokenizator** za razdvajanje teksta na **tokene**.\n",
"* Izgraditi **rječnik** tih tokena.\n",
"\n",
"### Ograničavanje veličine rječnika\n",
"\n",
"U primjeru s AG News skupom podataka, veličina rječnika je prilično velika, više od 100 tisuća riječi. Općenito govoreći, ne trebaju nam riječi koje se rijetko pojavljuju u tekstu — samo nekoliko rečenica će ih sadržavati, a model iz njih neće ništa naučiti. Stoga ima smisla ograničiti veličinu rječnika na manji broj tako da proslijedimo argument konstruktoru vektorizatora:\n",
"\n",
"Oba ova koraka mogu se obaviti pomoću sloja **TextVectorization**. Instancirajmo objekt vektorizatora, a zatim pozovimo metodu `adapt` kako bismo prošli kroz sav tekst i izgradili rječnik:\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": [
"> **Napomena** da koristimo samo podskup cijelog skupa podataka za izgradnju vokabulara. To radimo kako bismo ubrzali vrijeme izvršavanja i ne zadržavali vas. Međutim, preuzimamo rizik da neke riječi iz cijelog skupa podataka neće biti uključene u vokabular i bit će zanemarene tijekom treniranja. Dakle, korištenje cijele veličine vokabulara i prolazak kroz cijeli skup podataka tijekom `adapt` trebalo bi povećati konačnu točnost, ali ne značajno.\n",
"\n",
"Sada možemo pristupiti stvarnom vokabularu:\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": [
"Pomoću vektorizatora možemo lako kodirati bilo koji tekst u skup brojeva:\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": [
"## Reprezentacija teksta pomoću vreće riječi\n",
"\n",
"Budući da riječi prenose značenje, ponekad možemo razumjeti značenje nekog teksta samo promatrajući pojedinačne riječi, bez obzira na njihov redoslijed u rečenici. Na primjer, pri klasifikaciji vijesti, riječi poput *vrijeme* i *snijeg* vjerojatno ukazuju na *vremensku prognozu*, dok bi riječi poput *dionice* i *dolar* bile povezane s *financijskim vijestima*.\n",
"\n",
"**Vreća riječi** (BoW) je najjednostavnija tradicionalna reprezentacija vektora za razumijevanje. Svaka riječ povezana je s indeksom vektora, a element vektora sadrži broj pojavljivanja svake riječi u danom dokumentu.\n",
"\n",
"![Slika koja prikazuje kako je reprezentacija vektora vreće riječi prikazana u memoriji.](../../../../../translated_images/bag-of-words-example.606fc1738f1d7ba98a9d693e3bcd706c6e83fa7bf8221e6e90d1a206d82f2ea4.hr.png)\n",
"\n",
"> **Note**: BoW možete zamisliti i kao zbroj svih vektora kodiranih metodom \"jedan na jedan\" za pojedinačne riječi u tekstu.\n",
"\n",
"Ispod je primjer kako generirati reprezentaciju vreće riječi koristeći Python biblioteku 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": [
"Također možemo koristiti Keras vektorizator koji smo definirali gore, pretvarajući svaki broj riječi u one-hot kodiranje i zbrajajući sve te vektore:\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": [
"> **Napomena**: Možda ćete biti iznenađeni što se rezultat razlikuje od prethodnog primjera. Razlog je taj što u Keras primjeru duljina vektora odgovara veličini vokabulara, koji je izgrađen iz cijelog AG News skupa podataka, dok smo u Scikit Learn primjeru vokabular izgradili iz uzorka teksta u hodu.\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Treniranje BoW klasifikatora\n",
"\n",
"Sada kada smo naučili kako izraditi reprezentaciju teksta pomoću vreće riječi, idemo trenirati klasifikator koji je koristi. Prvo, trebamo naš skup podataka pretvoriti u reprezentaciju vreće riječi. To se može postići korištenjem funkcije `map` na sljedeći način:\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": [
"Sada definirajmo jednostavnu neuronsku mrežu klasifikatora koja sadrži jedan linearni sloj. Veličina ulaza je `vocab_size`, a veličina izlaza odgovara broju klasa (4). Budući da rješavamo zadatak klasifikacije, završna aktivacijska funkcija je **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": [
"Budući da imamo 4 klase, točnost iznad 80% je dobar rezultat.\n",
"\n",
"## Treniranje klasifikatora kao jedne mreže\n",
"\n",
"Budući da je vektorizator također Keras sloj, možemo definirati mrežu koja ga uključuje i trenirati je od početka do kraja. Na taj način ne moramo vektorizirati skup podataka koristeći `map`, već možemo jednostavno proslijediti izvorni skup podataka na ulaz mreže.\n",
"\n",
"> **Napomena**: I dalje bismo morali primijeniti mapiranja na naš skup podataka kako bismo polja iz rječnika (kao što su `title`, `description` i `label`) pretvorili u tuple. Međutim, prilikom učitavanja podataka s diska, možemo odmah izgraditi skup podataka s potrebnom strukturom.\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": [
"## Bigrami, trigrami i n-grami\n",
"\n",
"Jedno ograničenje pristupa vreće riječi (bag-of-words) je to što su neke riječi dio višerječnih izraza. Na primjer, riječ 'hot dog' ima potpuno drugačije značenje od riječi 'hot' i 'dog' u drugim kontekstima. Ako uvijek predstavljamo riječi 'hot' i 'dog' koristeći iste vektore, to može zbuniti naš model.\n",
"\n",
"Kako bismo to riješili, često se koriste **n-gramske reprezentacije** u metodama klasifikacije dokumenata, gdje je učestalost svake riječi, dvorječnih ili trovrječnih izraza korisna značajka za treniranje klasifikatora. U bigramskim reprezentacijama, na primjer, dodajemo sve parove riječi u vokabular, uz originalne riječi.\n",
"\n",
"Ispod je primjer kako generirati bigramsku reprezentaciju vreće riječi koristeći 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": [
"Glavni nedostatak pristupa n-grama je taj što veličina vokabulara počinje iznimno brzo rasti. U praksi, potrebno je kombinirati n-gram reprezentaciju s tehnikom smanjenja dimenzionalnosti, poput *ugradbi* (embeddings), o čemu ćemo raspravljati u sljedećoj jedinici.\n",
"\n",
"Kako bismo koristili n-gram reprezentaciju u našem **AG News** skupu podataka, trebamo proslijediti parametar `ngrams` našem konstruktoru `TextVectorization`. Duljina vokabulara bigrama je **znatno veća**, u našem slučaju više od 1,3 milijuna tokena! Stoga ima smisla ograničiti bigram tokene na neki razuman broj.\n",
"\n",
"Mogli bismo koristiti isti kod kao gore za treniranje klasifikatora, no to bi bilo vrlo neučinkovito u smislu memorije. U sljedećoj jedinici, trenirat ćemo klasifikator bigrama koristeći ugradbe. U međuvremenu, možete eksperimentirati s treniranjem klasifikatora bigrama u ovoj bilježnici i vidjeti možete li postići veću točnost.\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Automatsko izračunavanje BoW vektora\n",
"\n",
"U gornjem primjeru izračunali smo BoW vektore ručno zbrajajući one-hot kodiranja pojedinačnih riječi. Međutim, najnovija verzija TensorFlow-a omogućuje nam automatsko izračunavanje BoW vektora prosljeđivanjem parametra `output_mode='count` konstruktoru vektorizatora. Ovo značajno olakšava definiranje i treniranje našeg modela:\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": [
"## Frekvencija pojma - inverzna frekvencija dokumenta (TF-IDF)\n",
"\n",
"U BoW reprezentaciji, pojavljivanja riječi se ponderiraju istom tehnikom bez obzira na samu riječ. Međutim, jasno je da su učestale riječi poput *a* i *in* mnogo manje važne za klasifikaciju od specijaliziranih pojmova. U većini NLP zadataka neke riječi su relevantnije od drugih.\n",
"\n",
"**TF-IDF** označava **frekvenciju pojma - inverznu frekvenciju dokumenta**. To je varijacija vreće riječi, gdje se umjesto binarne vrijednosti 0/1 koja označava pojavljivanje riječi u dokumentu koristi vrijednost s pomičnim zarezom, koja je povezana s učestalošću pojavljivanja riječi u korpusu.\n",
"\n",
"Formalnije, težina $w_{ij}$ riječi $i$ u dokumentu $j$ definirana je kao:\n",
"$$\n",
"w_{ij} = tf_{ij}\\times\\log({N\\over df_i})\n",
"$$\n",
"gdje\n",
"* $tf_{ij}$ je broj pojavljivanja $i$ u $j$, tj. BoW vrijednost koju smo ranije vidjeli\n",
"* $N$ je broj dokumenata u zbirci\n",
"* $df_i$ je broj dokumenata koji sadrže riječ $i$ u cijeloj zbirci\n",
"\n",
"TF-IDF vrijednost $w_{ij}$ raste proporcionalno broju puta koliko se riječ pojavljuje u dokumentu i smanjuje se ovisno o broju dokumenata u korpusu koji sadrže tu riječ, što pomaže u prilagodbi činjenici da se neke riječi pojavljuju češće od drugih. Na primjer, ako se riječ pojavljuje u *svakom* dokumentu u zbirci, $df_i=N$, i $w_{ij}=0$, te bi ti pojmovi bili potpuno zanemareni.\n",
"\n",
"TF-IDF vektorizaciju teksta možete jednostavno kreirati koristeći 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": [
"U Kerasu, sloj `TextVectorization` može automatski izračunati TF-IDF frekvencije prosljeđivanjem parametra `output_mode='tf-idf'`. Ponovimo kod koji smo koristili gore kako bismo vidjeli povećava li korištenje TF-IDF-a točnost:\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": [
"## Zaključak\n",
"\n",
"Iako TF-IDF reprezentacije dodjeljuju težine frekvencijama različitih riječi, one nisu sposobne prikazati značenje ili redoslijed. Kao što je poznati lingvist J. R. Firth rekao 1935. godine: \"Potpuno značenje riječi uvijek je kontekstualno, i nijedno proučavanje značenja izvan konteksta ne može se smatrati ozbiljnim.\" Kasnije u tečaju naučit ćemo kako uhvatiti kontekstualne informacije iz teksta koristeći jezično modeliranje.\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"\n---\n\n**Odricanje od odgovornosti**: \nOvaj dokument je preveden korištenjem AI usluge za prevođenje [Co-op Translator](https://github.com/Azure/co-op-translator). Iako nastojimo osigurati točnost, imajte na umu da automatski prijevodi mogu sadržavati pogreške ili netočnosti. Izvorni dokument na izvornom jeziku treba smatrati mjerodavnim izvorom. Za ključne informacije preporučuje se profesionalni prijevod od strane stručnjaka. Ne preuzimamo odgovornost za bilo kakva nesporazuma ili pogrešna tumačenja koja mogu proizaći iz korištenja ovog prijevoda.\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-30T08:18:53+00:00",
"source_file": "lessons/5-NLP/13-TextRep/TextRepresentationTF.ipynb",
"language_code": "hr"
}
},
"nbformat": 4,
"nbformat_minor": 4
}

View File

@ -0,0 +1,726 @@
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Ugrađivanja\n",
"\n",
"U našem prethodnom primjeru radili smo s vektorima vreće riječi visoke dimenzionalnosti duljine `vocab_size`, te smo eksplicitno pretvarali vektore niskodimenzionalne pozicijske reprezentacije u rijetku one-hot reprezentaciju. Ova one-hot reprezentacija nije memorijski učinkovita, a osim toga, svaka se riječ tretira neovisno o drugima, tj. one-hot kodirani vektori ne izražavaju nikakvu semantičku sličnost između riječi.\n",
"\n",
"U ovoj jedinici nastavit ćemo istraživati **News AG** skup podataka. Za početak, učitajmo podatke i preuzmimo neke definicije iz prethodne bilježnice.\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": [
"## Što je ugradnja?\n",
"\n",
"Ideja **ugradnje** je predstavljati riječi pomoću nižedimenzionalnih gustih vektora, koji na neki način odražavaju semantičko značenje riječi. Kasnije ćemo raspraviti kako izgraditi smislene ugradnje riječi, ali za sada razmotrimo ugradnje kao način smanjenja dimenzionalnosti vektora riječi.\n",
"\n",
"Dakle, sloj za ugradnju uzima riječ kao ulaz i proizvodi izlazni vektor određene veličine `embedding_size`. Na neki način, to je vrlo slično sloju `Linear`, ali umjesto da koristi vektor kodiran kao one-hot, može uzeti broj riječi kao ulaz.\n",
"\n",
"Korištenjem sloja za ugradnju kao prvog sloja u našoj mreži, možemo prijeći s modela vreće riječi na model **vreće ugradnji**, gdje prvo pretvaramo svaku riječ u našem tekstu u odgovarajuću ugradnju, a zatim izračunavamo neku agregatnu funkciju nad svim tim ugradnjama, poput `sum`, `average` ili `max`.\n",
"\n",
"![Slika koja prikazuje klasifikator s ugradnjom za pet riječi u nizu.](../../../../../translated_images/embedding-classifier-example.b77f021a7ee67eeec8e68bfe11636c5b97d6eaa067515a129bfb1d0034b1ac5b.hr.png)\n",
"\n",
"Naša neuronska mreža za klasifikaciju započet će slojem za ugradnju, zatim slojem za agregaciju, i linearnim klasifikatorom na vrhu:\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": [
"### Rad s promjenjivom veličinom sekvenci varijabli\n",
"\n",
"Zbog ove arhitekture, minibatchovi za našu mrežu moraju se kreirati na određeni način. U prethodnoj jedinici, kada smo koristili vreću riječi (bag-of-words), svi BoW tenzori u minibatchu imali su jednaku veličinu `vocab_size`, bez obzira na stvarnu duljinu naše tekstualne sekvence. Kada prijeđemo na ugrađivanje riječi (word embeddings), završit ćemo s promjenjivim brojem riječi u svakom uzorku teksta, a prilikom kombiniranja tih uzoraka u minibatcheve morat ćemo primijeniti neko popunjavanje (padding).\n",
"\n",
"To se može učiniti korištenjem iste tehnike pružanja funkcije `collate_fn` izvoru podataka:\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": [
"### Treniranje klasifikatora ugrađivanja\n",
"\n",
"Sada kada smo definirali odgovarajući dataloader, možemo trenirati model koristeći funkciju za treniranje koju smo definirali u prethodnoj jedinici:\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": [
"> **Napomena**: Ovdje treniramo samo za 25 tisuća zapisa (manje od jednog punog epoha) radi uštede vremena, ali možete nastaviti s treniranjem, napisati funkciju za treniranje kroz nekoliko epoha i eksperimentirati s parametrom stope učenja kako biste postigli veću točnost. Trebali biste moći postići točnost od oko 90%.\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Sloj EmbeddingBag i prikaz sekvenci promjenjive duljine\n",
"\n",
"U prethodnoj arhitekturi morali smo popuniti sve sekvence na istu duljinu kako bismo ih uklopili u minibatch. Ovo nije najučinkovitiji način za prikazivanje sekvenci promjenjive duljine - drugi pristup bio bi korištenje **offset** vektora, koji bi sadržavao pomake svih sekvenci pohranjenih u jednom velikom vektoru.\n",
"\n",
"![Slika koja prikazuje prikaz sekvenci pomoću offseta](../../../../../translated_images/offset-sequence-representation.eb73fcefb29b46eecfbe74466077cfeb7c0f93a4f254850538a2efbc63517479.hr.png)\n",
"\n",
"> **Napomena**: Na slici iznad prikazana je sekvenca znakova, ali u našem primjeru radimo sa sekvencama riječi. Međutim, osnovni princip prikazivanja sekvenci pomoću offset vektora ostaje isti.\n",
"\n",
"Za rad s prikazom pomoću offseta koristimo sloj [`EmbeddingBag`](https://pytorch.org/docs/stable/generated/torch.nn.EmbeddingBag.html). Sličan je sloju `Embedding`, ali kao ulaz uzima sadržajni vektor i offset vektor, a također uključuje sloj za prosjek koji može biti `mean`, `sum` ili `max`.\n",
"\n",
"Evo izmijenjene mreže koja koristi `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": [
"Da bismo pripremili skup podataka za treniranje, moramo osigurati funkciju za konverziju koja će pripremiti vektor pomaka:\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": [
"Imajte na umu da, za razliku od svih prethodnih primjera, naša mreža sada prihvaća dva parametra: vektor podataka i vektor pomaka, koji su različitih veličina. Slično tome, naš učitavač podataka također nam pruža 3 vrijednosti umjesto 2: i tekstualni i vektori pomaka pružaju se kao značajke. Stoga trebamo malo prilagoditi našu funkciju treniranja kako bismo to uzeli u obzir:\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": [
"## Semantičke Ugradnje: Word2Vec\n",
"\n",
"U našem prethodnom primjeru, sloj za ugradnju modela naučio je mapirati riječi u vektorsku reprezentaciju, no ta reprezentacija nije imala puno semantičkog značenja. Bilo bi korisno naučiti takvu vektorsku reprezentaciju gdje bi slične riječi ili sinonimi odgovarali vektorima koji su blizu jedni drugima prema nekoj metričkoj udaljenosti (npr. euklidskoj udaljenosti).\n",
"\n",
"Da bismo to postigli, potrebno je unaprijed obučiti naš model za ugradnju na velikoj zbirci teksta na specifičan način. Jedan od prvih pristupa za obuku semantičkih ugradnji naziva se [Word2Vec](https://en.wikipedia.org/wiki/Word2vec). Temelji se na dvije glavne arhitekture koje se koriste za stvaranje distribuirane reprezentacije riječi:\n",
"\n",
" - **Kontinuirana vreća riječi** (CBoW) — u ovoj arhitekturi model se obučava da predvidi riječ iz okolnog konteksta. Za zadani ngram $(W_{-2},W_{-1},W_0,W_1,W_2)$, cilj modela je predvidjeti $W_0$ na temelju $(W_{-2},W_{-1},W_1,W_2)$.\n",
" - **Kontinuirani skip-gram** je suprotan CBoW-u. Model koristi okolni prozor kontekstualnih riječi kako bi predvidio trenutnu riječ.\n",
"\n",
"CBoW je brži, dok je skip-gram sporiji, ali bolje predstavlja riječi koje se rjeđe pojavljuju.\n",
"\n",
"![Slika koja prikazuje algoritme CBoW i Skip-Gram za pretvaranje riječi u vektore.](../../../../../translated_images/example-algorithms-for-converting-words-to-vectors.fbe9207a726922f6f0f5de66427e8a6eda63809356114e28fb1fa5f4a83ebda7.hr.png)\n",
"\n",
"Za eksperimentiranje s Word2Vec ugradnjom unaprijed obučenom na Google News skupu podataka, možemo koristiti biblioteku **gensim**. Ispod nalazimo riječi najsličnije riječi 'neural':\n",
"\n",
"> **Napomena:** Kada prvi put kreirate vektore riječi, njihovo preuzimanje može potrajati!\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": [
"Također možemo izračunati vektorske ugradnje iz riječi, koje će se koristiti za treniranje modela klasifikacije (prikazujemo samo prvih 20 komponenti vektora radi jasnoće):\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": [
"Sjajna stvar kod semantičkih ugradnji je da možete manipulirati vektorskim kodiranjem kako biste promijenili semantiku. Na primjer, možemo tražiti riječ čija bi vektorska reprezentacija bila što bliža riječima *kralj* i *žena*, a što udaljenija od riječi *muškarac*:\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": [
"Oba, CBoW i Skip-Grams, su \"prediktivni\" ugrađeni modeli, jer uzimaju u obzir samo lokalne kontekste. Word2Vec ne koristi globalni kontekst.\n",
"\n",
"**FastText** se nadovezuje na Word2Vec tako što uči vektorske reprezentacije za svaku riječ i znakove n-grama pronađene unutar svake riječi. Vrijednosti tih reprezentacija se zatim prosječno izračunavaju u jedan vektor pri svakom koraku treniranja. Iako ovo dodaje puno dodatnih izračuna tijekom pred-treniranja, omogućuje ugrađenim modelima riječi da kodiraju informacije o podriječima.\n",
"\n",
"Druga metoda, **GloVe**, koristi ideju matrice su-pojavljivanja i primjenjuje neuronske metode za dekompoziciju matrice su-pojavljivanja u izraženije i nelinearne vektore riječi.\n",
"\n",
"Možete se poigrati s primjerom mijenjajući ugrađene modele na FastText i GloVe, budući da gensim podržava nekoliko različitih modela za ugrađivanje riječi.\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Korištenje unaprijed istreniranih ugradnji u PyTorchu\n",
"\n",
"Možemo prilagoditi gornji primjer kako bismo unaprijed popunili matricu u našem sloju za ugradnju semantičkim ugradnjama, poput Word2Vec-a. Trebamo uzeti u obzir da se rječnici unaprijed istreniranih ugradnji i našeg tekstualnog korpusa vjerojatno neće podudarati, pa ćemo inicijalizirati težine za nedostajuće riječi nasumičnim vrijednostima:\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": [
"Sada trenirajmo naš model. Imajte na umu da je vrijeme potrebno za treniranje modela znatno duže nego u prethodnom primjeru, zbog veće veličine sloja za ugradnju, a time i znatno većeg broja parametara. Također, zbog toga ćemo možda morati trenirati naš model na više primjera ako želimo izbjeći prenaučenost.\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": [
"U našem slučaju ne vidimo značajan porast točnosti, što je vjerojatno zbog prilično različitih vokabulara. \n",
"Kako bismo prevladali problem različitih vokabulara, možemo koristiti jedno od sljedećih rješenja: \n",
"* Ponovno trenirati word2vec model na našem vokabularu \n",
"* Učitati naš skup podataka s vokabularom iz unaprijed treniranog word2vec modela. Vokabular koji se koristi za učitavanje skupa podataka može se specificirati tijekom učitavanja. \n",
"\n",
"Drugi pristup čini se jednostavnijim, posebno zato što PyTorch `torchtext` okvir sadrži ugrađenu podršku za ugrađivanja. Na primjer, možemo instancirati vokabular temeljen na GloVe-u na sljedeći način: \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": [
"Učitani rječnik ima sljedeće osnovne operacije:\n",
"* `vocab.stoi` rječnik omogućuje pretvaranje riječi u njezin indeks u rječniku\n",
"* `vocab.itos` radi suprotno - pretvara broj u riječ\n",
"* `vocab.vectors` je niz vektora ugrađivanja, pa da bismo dobili ugrađivanje riječi `s`, trebamo koristiti `vocab.vectors[vocab.stoi[s]]`\n",
"\n",
"Evo primjera manipulacije ugrađivanjima kako bismo demonstrirali jednadžbu **kind-man+woman = queen** (morao sam malo prilagoditi koeficijent da bi radilo):\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": [
"Da bismo obučili klasifikator koristeći te ugrađene vektore, prvo moramo kodirati naš skup podataka koristeći GloVe vokabular:\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": [
"Kao što smo vidjeli gore, svi vektorski ugrađaji pohranjeni su u matrici `vocab.vectors`. To čini izuzetno jednostavnim učitavanje tih težina u težine sloja za ugrađivanje pomoću jednostavnog kopiranja:\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": [
"Sada obučimo naš model i provjerimo hoćemo li dobiti bolje rezultate:\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": [
"Jedan od razloga zašto ne vidimo značajno povećanje točnosti je činjenica da neke riječi iz našeg skupa podataka nedostaju u unaprijed uvježbanom GloVe rječniku, te se stoga u biti ignoriraju. Kako bismo prevladali ovu činjenicu, možemo uvježbati vlastite ugradnje na našem skupu podataka.\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Kontekstualni Ugrađeni Vektori\n",
"\n",
"Jedno od ključnih ograničenja tradicionalnih unaprijed istreniranih reprezentacija ugrađenih vektora poput Word2Vec-a je problem razlučivanja značenja riječi. Iako unaprijed istrenirani ugrađeni vektori mogu uhvatiti dio značenja riječi u kontekstu, svako moguće značenje riječi kodirano je u isti vektor. To može uzrokovati probleme u modelima koji dolaze nakon, budući da mnoge riječi, poput riječi 'play', imaju različita značenja ovisno o kontekstu u kojem se koriste.\n",
"\n",
"Na primjer, riječ 'play' u ove dvije rečenice ima prilično različita značenja:\n",
"- Otišao sam na **predstavu** u kazalištu.\n",
"- John želi **igrati** se s prijateljima.\n",
"\n",
"Unaprijed istrenirani ugrađeni vektori gore predstavljaju oba ova značenja riječi 'play' u istom vektoru. Kako bismo prevladali ovo ograničenje, trebamo izgraditi ugrađene vektore temeljene na **jezičnom modelu**, koji je istreniran na velikom korpusu teksta i *zna* kako se riječi mogu kombinirati u različitim kontekstima. Rasprava o kontekstualnim ugrađenim vektorima izlazi izvan okvira ovog vodiča, ali ćemo im se vratiti kada budemo govorili o jezičnim modelima u sljedećoj jedinici.\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"\n---\n\n**Odricanje od odgovornosti**: \nOvaj dokument je preveden korištenjem AI usluge za prevođenje [Co-op Translator](https://github.com/Azure/co-op-translator). Iako nastojimo osigurati točnost, imajte na umu da automatski prijevodi mogu sadržavati pogreške ili netočnosti. Izvorni dokument na izvornom jeziku treba smatrati mjerodavnim izvorom. Za ključne informacije preporučuje se profesionalni prijevod od strane stručnjaka. Ne preuzimamo odgovornost za bilo kakve nesporazume ili pogrešne interpretacije proizašle iz korištenja ovog prijevoda.\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-30T08:15:10+00:00",
"source_file": "lessons/5-NLP/14-Embeddings/EmbeddingsPyTorch.ipynb",
"language_code": "hr"
}
},
"nbformat": 4,
"nbformat_minor": 2
}

View File

@ -0,0 +1,695 @@
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Ugrađivanja\n",
"\n",
"U našem prethodnom primjeru radili smo s vektorima vreće riječi visoke dimenzionalnosti duljine `vocab_size`, te smo eksplicitno pretvarali niskodimenzionalne vektore pozicijskog prikaza u rijetke one-hot prikaze. Ovaj one-hot prikaz nije memorijski učinkovit. Osim toga, svaka se riječ tretira neovisno o drugima, pa one-hot kodirani vektori ne izražavaju semantičke sličnosti između riječi.\n",
"\n",
"U ovoj jedinici nastavit ćemo istraživati **News AG** skup podataka. Za početak, učitajmo podatke i preuzmimo neke definicije iz prethodne jedinice.\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": [
"### Što je embedding?\n",
"\n",
"Ideja **embeddinga** je predstavljati riječi pomoću gusto raspoređenih vektora niže dimenzionalnosti koji odražavaju semantičko značenje riječi. Kasnije ćemo raspraviti kako izgraditi smislene word embeddinge, ali za sada razmotrimo embeddinge kao način smanjenja dimenzionalnosti vektora riječi.\n",
"\n",
"Dakle, embedding sloj uzima riječ kao ulaz i proizvodi izlazni vektor određene veličine `embedding_size`. Na neki način, vrlo je sličan `Dense` sloju, ali umjesto da uzima one-hot kodirani vektor kao ulaz, može uzeti broj riječi.\n",
"\n",
"Korištenjem embedding sloja kao prvog sloja u našoj mreži, možemo prijeći s modela vreće riječi (bag-of-words) na model **embedding vreće** (embedding bag), gdje prvo pretvaramo svaku riječ u našem tekstu u odgovarajući embedding, a zatim izračunavamo neku agregacijsku funkciju nad svim tim embeddingima, poput `sum`, `average` ili `max`.\n",
"\n",
"![Slika koja prikazuje embedding klasifikator za pet riječi u nizu.](../../../../../translated_images/embedding-classifier-example.b77f021a7ee67eeec8e68bfe11636c5b97d6eaa067515a129bfb1d0034b1ac5b.hr.png)\n",
"\n",
"Naša neuronska mreža klasifikatora sastoji se od sljedećih slojeva:\n",
"\n",
"* `TextVectorization` sloj, koji uzima string kao ulaz i proizvodi tenzor brojeva tokena. Odredit ćemo razumnu veličinu vokabulara `vocab_size` i zanemariti riječi koje se rjeđe koriste. Ulazni oblik bit će 1, a izlazni oblik bit će $n$, budući da ćemo dobiti $n$ tokena kao rezultat, pri čemu svaki od njih sadrži brojeve od 0 do `vocab_size`.\n",
"* `Embedding` sloj, koji uzima $n$ brojeva i smanjuje svaki broj na gusti vektor određene duljine (100 u našem primjeru). Tako će ulazni tenzor oblika $n$ biti transformiran u $n\\times 100$ tenzor.\n",
"* Agregacijski sloj, koji uzima prosjek ovog tenzora duž prve osi, tj. izračunat će prosjek svih $n$ ulaznih tenzora koji odgovaraju različitim riječima. Za implementaciju ovog sloja koristit ćemo `Lambda` sloj i u njega proslijediti funkciju za izračunavanje prosjeka. Izlaz će imati oblik 100 i predstavljat će numeričku reprezentaciju cijelog ulaznog niza.\n",
"* Završni `Dense` linearni klasifikator.\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": [
"U ispisu `summary`, u stupcu **output shape**, prva dimenzija tenzora `None` odgovara veličini minibatcha, a druga odgovara duljini niza tokena. Svi nizovi tokena u minibatchu imaju različite duljine. O tome kako se nositi s tim razgovarat ćemo u sljedećem odjeljku.\n",
"\n",
"Sada krenimo s treniranjem mreže:\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": [
"> **Napomena** da gradimo vektorizator na temelju podskupa podataka. To se radi kako bi se ubrzao proces, i to može rezultirati situacijom u kojoj nisu svi tokeni iz našeg teksta prisutni u rječniku. U tom slučaju, ti tokeni će biti zanemareni, što može dovesti do neznatno manje točnosti. Međutim, u stvarnom životu podskup teksta često daje dobru procjenu rječnika.\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Rad s različitim veličinama sekvenci varijabli\n",
"\n",
"Razumijmo kako se odvija treniranje u minibatchovima. U gornjem primjeru, ulazni tenzor ima dimenziju 1, a koristimo minibatcheve duljine 128, tako da je stvarna veličina tenzora $128 \\times 1$. Međutim, broj tokena u svakoj rečenici je različit. Ako primijenimo sloj `TextVectorization` na jedan ulaz, broj vraćenih tokena bit će različit, ovisno o tome kako je tekst tokeniziran:\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": [
"Međutim, kada primijenimo vektorizator na nekoliko sekvenci, mora proizvesti tenzor pravokutnog oblika, pa popunjava neiskorištene elemente s PAD tokenom (što je u našem slučaju nula):\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": [
"Ovdje možemo vidjeti ugrađivanja:\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": [
"> **Napomena**: Kako bi se smanjila količina popunjavanja, u nekim slučajevima ima smisla poredati sve sekvence u skupu podataka prema rastućoj duljini (ili, točnije, prema broju tokena). To će osigurati da svaki minibatch sadrži sekvence slične duljine.\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Semantičke ugradnje: Word2Vec\n",
"\n",
"U našem prethodnom primjeru, sloj za ugradnju naučio je mapirati riječi u vektorske reprezentacije, no te reprezentacije nisu imale semantičko značenje. Bilo bi korisno naučiti vektorsku reprezentaciju na način da slične riječi ili sinonimi odgovaraju vektorima koji su blizu jedni drugima prema nekoj metričkoj udaljenosti (na primjer, euklidskoj udaljenosti).\n",
"\n",
"Da bismo to postigli, potrebno je unaprijed istrenirati naš model za ugradnju na velikoj zbirci teksta koristeći tehniku poput [Word2Vec](https://en.wikipedia.org/wiki/Word2vec). Ova tehnika temelji se na dvije glavne arhitekture koje se koriste za stvaranje distribuirane reprezentacije riječi:\n",
"\n",
" - **Kontinuirana vreća riječi** (CBoW), gdje treniramo model da predvidi riječ iz okolnog konteksta. Za zadani ngram $(W_{-2},W_{-1},W_0,W_1,W_2)$, cilj modela je predvidjeti $W_0$ na temelju $(W_{-2},W_{-1},W_1,W_2)$.\n",
" - **Kontinuirani skip-gram** je suprotan CBoW-u. Model koristi okolni prozor kontekstualnih riječi kako bi predvidio trenutnu riječ.\n",
"\n",
"CBoW je brži, dok je skip-gram sporiji, ali bolje predstavlja riječi koje se rjeđe pojavljuju.\n",
"\n",
"![Slika koja prikazuje algoritme CBoW i Skip-Gram za pretvaranje riječi u vektore.](../../../../../translated_images/example-algorithms-for-converting-words-to-vectors.fbe9207a726922f6f0f5de66427e8a6eda63809356114e28fb1fa5f4a83ebda7.hr.png)\n",
"\n",
"Kako bismo eksperimentirali s Word2Vec ugradnjom unaprijed istreniranom na Google News skupu podataka, možemo koristiti biblioteku **gensim**. Ispod nalazimo riječi koje su najsličnije riječi 'neural'.\n",
"\n",
"> **Napomena:** Kada prvi put kreirate vektore riječi, njihovo preuzimanje može potrajati!\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": [
"Također možemo izdvojiti vektorsko urezivanje iz riječi, koje će se koristiti za treniranje modela klasifikacije. Urezivanje ima 300 komponenti, ali ovdje prikazujemo samo prvih 20 komponenti vektora radi jasnoće:\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": [
"Sjajna stvar kod semantičkih ugradnji je da možete manipulirati vektorskim kodiranjem na temelju semantike. Na primjer, možemo tražiti riječ čija je vektorska reprezentacija što je moguće bliža riječima *kralj* i *žena*, a što je moguće udaljenija od riječi *muškarac*:\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": [
"Primjer iznad koristi neku internu GenSym magiju, ali osnovna logika je zapravo prilično jednostavna. Zanimljiva stvar kod ugradnji (embeddings) je da možete izvoditi normalne vektorske operacije na vektorskim ugradnjama, a to bi odražavalo operacije na značenjima riječi. Primjer iznad može se izraziti u terminima vektorskih operacija: izračunavamo vektor koji odgovara **KRALJ-MUŠKARAC+ŽENA** (operacije `+` i `-` izvode se na vektorskim prikazima odgovarajućih riječi), a zatim pronalazimo najbližu riječ u rječniku tom vektoru:\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": [
"> **NAPOMENA**: Morali smo dodati male koeficijente na vektore *man* i *woman* - pokušajte ih ukloniti da vidite što će se dogoditi.\n",
"\n",
"Kako bismo pronašli najbliži vektor, koristimo TensorFlow mehanizam za izračunavanje vektora udaljenosti između našeg vektora i svih vektora u rječniku, a zatim pronalazimo indeks minimalne riječi koristeći `argmin`.\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Iako Word2Vec izgleda kao izvrstan način za izražavanje semantike riječi, ima mnoge nedostatke, uključujući sljedeće:\n",
"\n",
"* I CBoW i skip-gram modeli su **prediktivni ugrađeni vektori**, i uzimaju u obzir samo lokalni kontekst. Word2Vec ne koristi prednosti globalnog konteksta.\n",
"* Word2Vec ne uzima u obzir **morfologiju** riječi, tj. činjenicu da značenje riječi može ovisiti o različitim dijelovima riječi, poput korijena.\n",
"\n",
"**FastText** pokušava prevladati drugo ograničenje i nadograđuje se na Word2Vec učenjem vektorskih prikaza za svaku riječ i n-grama znakova unutar svake riječi. Vrijednosti tih prikaza zatim se prosječe u jedan vektor pri svakom koraku treniranja. Iako ovo dodaje puno dodatnih izračuna tijekom prethodnog treniranja, omogućuje ugrađenim vektorima riječi da kodiraju informacije o podriječima.\n",
"\n",
"Druga metoda, **GloVe**, koristi drugačiji pristup ugrađivanju riječi, temeljen na faktorizaciji matrice konteksta riječi. Prvo, gradi veliku matricu koja broji koliko se puta riječi pojavljuju u različitim kontekstima, a zatim pokušava predstaviti tu matricu u nižim dimenzijama na način koji minimizira gubitak rekonstrukcije.\n",
"\n",
"Knjižnica gensim podržava te ugrađene vektore riječi, i možete eksperimentirati s njima mijenjajući kod za učitavanje modela iznad.\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Korištenje unaprijed naučenih ugradnji u Kerasu\n",
"\n",
"Možemo prilagoditi gornji primjer kako bismo unaprijed popunili matricu u našem sloju za ugradnju semantičkim ugradnjama, poput Word2Vec-a. Vokabulari unaprijed naučene ugradnje i tekstnog korpusa vjerojatno se neće podudarati, pa moramo odabrati jedan. Ovdje istražujemo dvije moguće opcije: korištenje vokabulara tokenizatora i korištenje vokabulara iz Word2Vec ugradnji.\n",
"\n",
"### Korištenje vokabulara tokenizatora\n",
"\n",
"Kada koristimo vokabular tokenizatora, neki od riječi iz vokabulara imat će odgovarajuće Word2Vec ugradnje, dok će neke nedostajati. S obzirom na to da je veličina našeg vokabulara `vocab_size`, a duljina vektora Word2Vec ugradnje `embed_size`, sloj za ugradnju bit će predstavljen težinskom matricom oblika `vocab_size`$\\times$`embed_size`. Ovu matricu ćemo popuniti prolaskom kroz vokabular:\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": [
"Za riječi koje nisu prisutne u Word2Vec rječniku, možemo ih ili ostaviti kao nule, ili generirati nasumični vektor.\n",
"\n",
"Sada možemo definirati sloj za ugradnju s unaprijed istreniranim težinama:\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": [
"> **Napomena**: Primijetite da smo postavili `trainable=False` prilikom kreiranja `Embedding`, što znači da ne treniramo ponovno sloj Embedding. Ovo može uzrokovati blago smanjenje točnosti, ali ubrzava proces treniranja.\n",
"\n",
"### Korištenje vokabulara za ugradnju\n",
"\n",
"Jedan od problema s prethodnim pristupom je taj što se vokabulari korišteni u TextVectorization i Embedding razlikuju. Kako bismo prevladali ovaj problem, možemo koristiti jedno od sljedećih rješenja:\n",
"* Ponovno trenirati Word2Vec model na našem vokabularu.\n",
"* Učitati naš skup podataka koristeći vokabular iz unaprijed istreniranog Word2Vec modela. Vokabulari koji se koriste za učitavanje skupa podataka mogu se specificirati tijekom učitavanja.\n",
"\n",
"Drugi pristup čini se jednostavnijim, pa ćemo ga implementirati. Prije svega, kreirat ćemo sloj `TextVectorization` sa specificiranim vokabularom, preuzetim iz Word2Vec ugradnji:\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": [
"Knjižnica gensim word embeddings sadrži praktičnu funkciju, `get_keras_embeddings`, koja će automatski stvoriti odgovarajući Keras embeddings sloj za vas.\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": [
"Jedan od razloga zašto ne vidimo veću točnost je taj što neke riječi iz našeg skupa podataka nedostaju u unaprijed uvježbanom GloVe rječniku, i stoga su u biti zanemarene. Kako bismo to prevladali, možemo uvježbati vlastite ugrađene vektore na temelju našeg skupa podataka.\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Kontekstualni ugrađeni prikazi\n",
"\n",
"Jedno od ključnih ograničenja tradicionalnih unaprijed istreniranih prikaza ugrađivanja, poput Word2Vec-a, jest činjenica da, iako mogu uhvatiti neko značenje riječi, ne mogu razlikovati različita značenja. To može uzrokovati probleme u modelima koji se koriste kasnije.\n",
"\n",
"Na primjer, riječ 'play' ima različita značenja u ove dvije rečenice:\n",
"- Otišao sam na **predstavu** u kazalištu.\n",
"- John želi **igrati** se sa svojim prijateljima.\n",
"\n",
"Unaprijed istrenirana ugrađivanja o kojima smo govorili predstavljaju oba značenja riječi 'play' u istom ugrađivanju. Kako bismo prevladali ovo ograničenje, trebamo izgraditi ugrađivanja temeljena na **jezičnom modelu**, koji je istreniran na velikom korpusu teksta i *zna* kako se riječi mogu povezivati u različitim kontekstima. Rasprava o kontekstualnim ugrađivanjima izlazi izvan okvira ovog vodiča, ali ćemo im se vratiti kada budemo govorili o jezičnim modelima u sljedećoj jedinici.\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"\n---\n\n**Odricanje od odgovornosti**: \nOvaj dokument je preveden korištenjem AI usluge za prevođenje [Co-op Translator](https://github.com/Azure/co-op-translator). Iako nastojimo osigurati točnost, imajte na umu da automatski prijevodi mogu sadržavati pogreške ili netočnosti. Izvorni dokument na izvornom jeziku treba smatrati mjerodavnim izvorom. Za ključne informacije preporučuje se profesionalni prijevod od strane stručnjaka. Ne preuzimamo odgovornost za bilo kakva nesporazuma ili pogrešna tumačenja koja mogu proizaći iz korištenja ovog prijevoda.\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-30T08:12:49+00:00",
"source_file": "lessons/5-NLP/14-Embeddings/EmbeddingsTF.ipynb",
"language_code": "hr"
}
},
"nbformat": 4,
"nbformat_minor": 4
}

View File

@ -0,0 +1,576 @@
{
"cells": [
{
"cell_type": "markdown",
"metadata": {
"id": "NXTSugt6ieXh"
},
"source": [
"## Treniranje CBoW modela\n",
"\n",
"Ova bilježnica dio je [AI za početnike kurikuluma](http://aka.ms/ai-beginners)\n",
"\n",
"U ovom primjeru, pogledat ćemo kako trenirati CBoW jezični model kako bismo dobili vlastiti Word2Vec prostor za ugrađivanje. Koristit ćemo AG News dataset kao izvor teksta.\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": [
"Prvo učitajmo naš skup podataka i definirajmo tokenizer i vokabular. Postavit ćemo `vocab_size` na 5000 kako bismo malo ograničili izračune.\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 model\n",
"\n",
"CBoW uči predviđati riječ na temelju $2N$ susjednih riječi. Na primjer, kada je $N=1$, dobit ćemo sljedeće parove iz rečenice *I like to train networks*: (like, I), (I, like), (to, like), (like, to), (train, to), (to, train), (networks, train), (train, networks). Ovdje je prva riječ susjedna riječ koja se koristi kao ulaz, a druga riječ je ona koju predviđamo.\n",
"\n",
"Kako bismo izgradili mrežu za predviđanje sljedeće riječi, trebamo pružiti susjednu riječ kao ulaz i dobiti broj riječi kao izlaz. Arhitektura CBoW mreže izgleda ovako:\n",
"\n",
"* Ulazna riječ prolazi kroz sloj za ugrađivanje (embedding layer). Ovaj sloj za ugrađivanje bit će naša Word2Vec ugrađenost, stoga ćemo ga definirati zasebno kao varijablu `embedder`. U ovom primjeru koristit ćemo veličinu ugrađivanja = 30, iako biste možda željeli eksperimentirati s većim dimenzijama (pravi Word2Vec ima 300).\n",
"* Vektor ugrađivanja zatim se prosljeđuje kroz linearni sloj koji će predvidjeti izlaznu riječ. Stoga ima `vocab_size` neurona.\n",
"\n",
"Za izlaz, ako koristimo `CrossEntropyLoss` kao funkciju gubitka, također ćemo morati pružiti samo brojeve riječi kao očekivane rezultate, bez one-hot kodiranja.\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": [
"## Priprema podataka za treniranje\n",
"\n",
"Sada ćemo programirati glavnu funkciju koja će izračunavati CBoW parove riječi iz teksta. Ova funkcija omogućit će nam da odredimo veličinu prozora i vratit će skup parova - ulaznu i izlaznu riječ. Imajte na umu da se ova funkcija može koristiti na riječima, kao i na vektorima/tensorima - što će nam omogućiti kodiranje teksta prije nego što ga proslijedimo funkciji `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": [
"Pripremimo skup podataka za treniranje. Proći ćemo kroz sve vijesti, pozvati `to_cbow` kako bismo dobili popis parova riječi, i dodati te parove u `X` i `Y`. Radi uštede vremena, razmotrit ćemo samo prvih 10k vijesti - lako možete ukloniti ovo ograničenje ako imate više vremena za čekanje i želite dobiti bolje ugrađivanja :)\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": [
"Također ćemo pretvoriti te podatke u jedan skup podataka i kreirati 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": [
"Također ćemo pretvoriti te podatke u jedan skup podataka i stvoriti učitavač podataka:\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": [
"Sada krenimo s pravim treniranjem. Koristit ćemo optimizer `SGD` s prilično visokom stopom učenja. Također možete isprobati i druge optimizere, poput `Adam`. Trenirat ćemo 10 epoha za početak - i možete ponovno pokrenuti ovu ćeliju ako želite još manji gubitak.\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": [
"## Isprobavanje Word2Vec-a\n",
"\n",
"Za korištenje Word2Vec-a, izdvojimo vektore koji odgovaraju svim riječima u našem vokabularu:\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": [
"Pogledajmo, na primjer, kako se riječ **Pariz** kodira u vektor:\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": [
"Zanimljivo je koristiti Word2Vec za traženje sinonima. Sljedeća funkcija će vratiti `n` najbližih riječi za dani unos. Kako bismo ih pronašli, izračunavamo normu $|w_i - v|$, gdje je $v$ vektor koji odgovara našoj ulaznoj riječi, a $w_i$ je kodiranje $i$-te riječi u vokabularu. Zatim sortiramo niz i vraćamo odgovarajuće indekse koristeći `argsort`, te uzimamo prvih `n` elemenata liste, koji kodiraju pozicije najbližih riječi u vokabularu.\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": [
"## Ključne točke\n",
"\n",
"Koristeći pametne tehnike poput CBoW-a, možemo trenirati Word2Vec model. Također možete pokušati trenirati skip-gram model koji je osposobljen za predviđanje susjedne riječi na temelju središnje, i provjeriti koliko dobro funkcionira.\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"\n---\n\n**Odricanje od odgovornosti**: \nOvaj dokument je preveden korištenjem AI usluge za prevođenje [Co-op Translator](https://github.com/Azure/co-op-translator). Iako nastojimo osigurati točnost, imajte na umu da automatski prijevodi mogu sadržavati pogreške ili netočnosti. Izvorni dokument na izvornom jeziku treba smatrati mjerodavnim izvorom. Za ključne informacije preporučuje se profesionalni prijevod od strane stručnjaka. Ne preuzimamo odgovornost za bilo kakve nesporazume ili pogrešne interpretacije proizašle iz korištenja ovog prijevoda.\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-30T08:01:31+00:00",
"source_file": "lessons/5-NLP/15-LanguageModeling/CBoW-PyTorch.ipynb",
"language_code": "hr"
}
},
"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": [
"# Rekurentne neuronske mreže\n",
"\n",
"U prethodnom modulu koristili smo bogate semantičke reprezentacije teksta i jednostavan linearni klasifikator na vrhu ugrađenih vektora. Ova arhitektura hvata agregirano značenje riječi u rečenici, ali ne uzima u obzir **redoslijed** riječi, jer operacija agregacije na vrhu ugrađenih vektora uklanja tu informaciju iz izvornog teksta. Budući da ti modeli ne mogu modelirati redoslijed riječi, nisu sposobni riješiti složenije ili dvosmislene zadatke poput generiranja teksta ili odgovaranja na pitanja.\n",
"\n",
"Kako bismo uhvatili značenje sekvenci teksta, trebamo koristiti drugu arhitekturu neuronske mreže, koja se naziva **rekurentna neuronska mreža** ili RNN. U RNN-u, rečenicu prosljeđujemo kroz mrežu jedan simbol po simbol, a mreža proizvodi određeno **stanje**, koje zatim ponovno prosljeđujemo mreži zajedno sa sljedećim simbolom.\n",
"\n",
"S obzirom na ulaznu sekvencu tokena $X_0,\\dots,X_n$, RNN stvara sekvencu blokova neuronske mreže i trenira ovu sekvencu od početka do kraja koristeći povratnu propagaciju. Svaki blok mreže uzima par $(X_i,S_i)$ kao ulaz i proizvodi $S_{i+1}$ kao rezultat. Konačno stanje $S_n$ ili izlaz $X_n$ ide u linearni klasifikator kako bi se proizveo rezultat. Svi blokovi mreže dijele iste težine i treniraju se od početka do kraja koristeći jednu povratnu propagaciju.\n",
"\n",
"Budući da se vektori stanja $S_0,\\dots,S_n$ prosljeđuju kroz mrežu, ona može naučiti sekvencijalne ovisnosti između riječi. Na primjer, kada se riječ *ne* pojavi negdje u sekvenci, mreža može naučiti negirati određene elemente unutar vektora stanja, što rezultira negacijom.\n",
"\n",
"> Budući da svi blokovi RNN-a na slici dijele iste težine, ista slika može se prikazati kao jedan blok (desno) s povratnom petljom, koja prosljeđuje izlazno stanje mreže natrag na ulaz.\n",
"\n",
"Pogledajmo kako rekurentne neuronske mreže mogu pomoći u klasifikaciji našeg skupa podataka o vijestima.\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": [
"## Jednostavni RNN klasifikator\n",
"\n",
"Kod jednostavnog RNN-a, svaka rekurentna jedinica je jednostavna linearna mreža koja uzima spojeni ulazni vektor i vektor stanja te proizvodi novi vektor stanja. PyTorch predstavlja ovu jedinicu s klasom `RNNCell`, a mreže takvih jedinica - kao sloj `RNN`.\n",
"\n",
"Za definiranje RNN klasifikatora, prvo ćemo primijeniti sloj za ugrađivanje kako bismo smanjili dimenzionalnost ulaznog vokabulara, a zatim dodati RNN sloj na vrh:\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": [
"> **Napomena:** Ovdje koristimo neuvježbani sloj ugradnje radi jednostavnosti, ali za još bolje rezultate možemo koristiti unaprijed uvježbani sloj ugradnje s Word2Vec ili GloVe ugradnjama, kako je opisano u prethodnoj jedinici. Za bolje razumijevanje, možda biste željeli prilagoditi ovaj kod kako bi radio s unaprijed uvježbanim ugradnjama.\n",
"\n",
"U našem slučaju koristit ćemo učitavač podataka s popunjavanjem (padded data loader), tako da će svaki batch sadržavati određeni broj sekvenci iste duljine. RNN sloj će primiti sekvencu tenzora ugradnje i proizvesti dva izlaza:\n",
"* $x$ je sekvenca izlaza RNN ćelija na svakom koraku\n",
"* $h$ je konačno skriveno stanje za zadnji element sekvence\n",
"\n",
"Zatim primjenjujemo potpuno povezani linearni klasifikator kako bismo dobili broj klase.\n",
"\n",
"> **Napomena:** RNN-ove je prilično teško trenirati jer, kada se RNN ćelije razviju duž duljine sekvence, broj slojeva uključenih u povratnu propagaciju postaje prilično velik. Stoga trebamo odabrati malu stopu učenja i trenirati mrežu na većem skupu podataka kako bismo postigli dobre rezultate. To može potrajati dosta dugo, pa je preporučljivo koristiti 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": [
"## Long Short Term Memory (LSTM)\n",
"\n",
"Jedan od glavnih problema klasičnih RNN-a je takozvani problem **nestajućih gradijenata**. Budući da se RNN-i treniraju od kraja do početka u jednom prolazu unatrag, teško prenose pogrešku do prvih slojeva mreže, zbog čega mreža ne može naučiti odnose između udaljenih tokena. Jedan od načina za izbjegavanje ovog problema je uvođenje **eksplicitnog upravljanja stanjem** pomoću takozvanih **vrata**. Dvije najpoznatije arhitekture ovog tipa su: **Long Short Term Memory** (LSTM) i **Gated Relay Unit** (GRU).\n",
"\n",
"![Slika koja prikazuje primjer ćelije Long Short Term Memory](../../../../../lessons/5-NLP/16-RNN/images/long-short-term-memory-cell.svg)\n",
"\n",
"LSTM mreža organizirana je na način sličan RNN-u, ali postoje dva stanja koja se prenose iz sloja u sloj: stvarno stanje $c$ i skriveni vektor $h$. U svakoj jedinici, skriveni vektor $h_i$ se spaja s ulazom $x_i$, i oni kontroliraju što se događa sa stanjem $c$ putem **vrata**. Svaka vrata su neuronska mreža sa sigmoidnom aktivacijom (izlaz u rasponu $[0,1]$), koja se može smatrati bitmaskom kada se pomnoži s vektorom stanja. Postoje sljedeća vrata (s lijeva na desno na slici iznad):\n",
"* **vrata zaborava** uzimaju skriveni vektor i određuju koje komponente vektora $c$ trebamo zaboraviti, a koje proslijediti dalje.\n",
"* **ulazna vrata** uzimaju neke informacije iz ulaza i skrivenog vektora te ih ubacuju u stanje.\n",
"* **izlazna vrata** transformiraju stanje putem nekog linearnog sloja s $\\tanh$ aktivacijom, zatim odabiru neke od njegovih komponenti koristeći skriveni vektor $h_i$ kako bi proizveli novo stanje $c_{i+1}$.\n",
"\n",
"Komponente stanja $c$ mogu se smatrati nekim zastavicama koje se mogu uključiti ili isključiti. Na primjer, kada u sekvenci naiđemo na ime *Alice*, možda ćemo pretpostaviti da se odnosi na ženski lik i podići zastavicu u stanju koja označava da imamo ženski imenicu u rečenici. Kada kasnije naiđemo na frazu *and Tom*, podići ćemo zastavicu koja označava da imamo množinsku imenicu. Tako manipulacijom stanja možemo navodno pratiti gramatička svojstva dijelova rečenice.\n",
"\n",
"> **Note**: Odličan resurs za razumijevanje unutarnje strukture LSTM-a je ovaj sjajan članak [Understanding LSTM Networks](https://colah.github.io/posts/2015-08-Understanding-LSTMs/) Christophera Olaha.\n",
"\n",
"Iako unutarnja struktura LSTM ćelije može izgledati složeno, PyTorch skriva ovu implementaciju unutar klase `LSTMCell` i pruža objekt `LSTM` za predstavljanje cijelog LSTM sloja. Stoga će implementacija LSTM klasifikatora biti prilično slična jednostavnom RNN-u koji smo vidjeli ranije:\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": [
"## Pakirane sekvence\n",
"\n",
"U našem primjeru morali smo popuniti sve sekvence u minibatchu nulama. Iako to rezultira određenim gubitkom memorije, kod RNN-a je još kritičnije što se dodatne RNN ćelije stvaraju za popunjene ulazne stavke, koje sudjeluju u treningu, ali ne nose nikakve važne ulazne informacije. Bilo bi puno bolje trenirati RNN samo do stvarne duljine sekvence.\n",
"\n",
"Kako bismo to postigli, u PyTorchu je uveden poseban format za pohranu popunjenih sekvenci. Pretpostavimo da imamo ulazni popunjeni minibatch koji izgleda ovako:\n",
"```\n",
"[[1,2,3,4,5],\n",
" [6,7,8,0,0],\n",
" [9,0,0,0,0]]\n",
"```\n",
"Ovdje 0 predstavlja popunjene vrijednosti, a stvarni vektor duljina ulaznih sekvenci je `[5,3,1]`.\n",
"\n",
"Kako bismo učinkovito trenirali RNN s popunjenim sekvencama, želimo započeti trening prve grupe RNN ćelija s velikim minibatchom (`[1,6,9]`), ali zatim završiti obradu treće sekvence i nastaviti trening s kraćim minibatchevima (`[2,7]`, `[3,8]`), i tako dalje. Dakle, pakirana sekvenca je predstavljena kao jedan vektor - u našem slučaju `[1,6,9,2,7,3,8,4,5]`, i vektor duljina (`[5,3,1]`), iz kojeg lako možemo rekonstruirati originalni popunjeni minibatch.\n",
"\n",
"Za stvaranje pakirane sekvence možemo koristiti funkciju `torch.nn.utils.rnn.pack_padded_sequence`. Sve rekurzivne slojeve, uključujući RNN, LSTM i GRU, podržavaju pakirane sekvence kao ulaz i proizvode pakirani izlaz, koji se može dekodirati pomoću `torch.nn.utils.rnn.pad_packed_sequence`.\n",
"\n",
"Kako bismo mogli proizvesti pakiranu sekvencu, trebamo proslijediti vektor duljina mreži, i stoga nam je potrebna drugačija funkcija za pripremu minibatcheva:\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": [
"Stvarna mreža bila bi vrlo slična `LSTMClassifier` gore, ali `forward` prolaz će primiti i podskup s podacima (padded minibatch) i vektor duljina sekvenci. Nakon izračuna ugradnje (embedding), izračunavamo zapakiranu sekvencu, prosljeđujemo je LSTM sloju, a zatim rezultat ponovno raspakiramo.\n",
"\n",
"> **Napomena**: Zapravo ne koristimo raspakirani rezultat `x`, jer koristimo izlaz iz skrivenih slojeva u sljedećim izračunima. Stoga možemo u potpunosti ukloniti raspakiranje iz ovog koda. Razlog zašto ga ovdje uključujemo je taj da vam omogućimo jednostavno modificiranje ovog koda, u slučaju da trebate koristiti izlaz mreže u daljnjim izračunima.\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": [
"> **Napomena:** Možda ste primijetili parametar `use_pack_sequence` koji prosljeđujemo funkciji za treniranje. Trenutno, funkcija `pack_padded_sequence` zahtijeva da tensor duljine sekvence bude na CPU uređaju, te stoga funkcija za treniranje mora izbjeći premještanje podataka o duljini sekvence na GPU tijekom treniranja. Možete pogledati implementaciju funkcije `train_emb` u datoteci [`torchnlp.py`](../../../../../lessons/5-NLP/16-RNN/torchnlp.py).\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Dvosmjerni i višeslojni RNN-ovi\n",
"\n",
"U našim primjerima, sve rekurentne mreže radile su u jednom smjeru, od početka sekvence do kraja. To izgleda prirodno, jer podsjeća na način na koji čitamo i slušamo govor. Međutim, budući da u mnogim praktičnim slučajevima imamo nasumičan pristup ulaznoj sekvenci, moglo bi imati smisla pokrenuti rekurentne izračune u oba smjera. Takve mreže nazivaju se **dvosmjerni** RNN-ovi, a mogu se stvoriti dodavanjem parametra `bidirectional=True` konstruktoru RNN/LSTM/GRU.\n",
"\n",
"Kod rada s dvosmjernom mrežom, trebala bi nam dva vektora skrivenog stanja, po jedan za svaki smjer. PyTorch kodira te vektore kao jedan vektor dvostruko veće veličine, što je prilično praktično, jer biste obično proslijedili dobiveno skriveno stanje potpuno povezanoj linearnoj sloju, i samo trebate uzeti u obzir ovo povećanje veličine prilikom stvaranja sloja.\n",
"\n",
"Rekurentna mreža, bilo jednosmjerna ili dvosmjerna, hvata određene uzorke unutar sekvence i može ih pohraniti u vektor stanja ili proslijediti u izlaz. Kao i kod konvolucijskih mreža, možemo izgraditi još jedan rekurentni sloj na vrhu prvog kako bismo uhvatili uzorke višeg nivoa, izgrađene od uzoraka nižeg nivoa koje je izvukao prvi sloj. To nas dovodi do pojma **višeslojni RNN**, koji se sastoji od dvije ili više rekurentnih mreža, gdje se izlaz prethodnog sloja prosljeđuje sljedećem sloju kao ulaz.\n",
"\n",
"![Slika koja prikazuje višeslojni long-short-term-memory RNN](../../../../../translated_images/multi-layer-lstm.dd975e29bb2a59fe58b429db833932d734c81f211cad2783797a9608984acb8c.hr.jpg)\n",
"\n",
"*Slika iz [ovog sjajnog posta](https://towardsdatascience.com/from-a-lstm-cell-to-a-multilayer-lstm-network-with-pytorch-2899eb5696f3) autora Fernanda Lópeza*\n",
"\n",
"PyTorch olakšava izgradnju takvih mreža, jer samo trebate dodati parametar `num_layers` konstruktoru RNN/LSTM/GRU kako biste automatski izgradili nekoliko slojeva rekurencije. To bi također značilo da će se veličina vektora skrivenog stanja proporcionalno povećati, i trebate to uzeti u obzir prilikom rukovanja izlazom rekurentnih slojeva.\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## RNN-ovi za druge zadatke\n",
"\n",
"U ovoj jedinici vidjeli smo da se RNN-ovi mogu koristiti za klasifikaciju sekvenci, ali zapravo mogu obraditi mnogo više zadataka, poput generiranja teksta, strojnog prevođenja i drugih. Te zadatke ćemo razmotriti u sljedećoj jedinici.\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"\n---\n\n**Odricanje od odgovornosti**: \nOvaj dokument je preveden korištenjem AI usluge za prevođenje [Co-op Translator](https://github.com/Azure/co-op-translator). Iako nastojimo osigurati točnost, imajte na umu da automatski prijevodi mogu sadržavati pogreške ili netočnosti. Izvorni dokument na izvornom jeziku treba smatrati mjerodavnim izvorom. Za ključne informacije preporučuje se profesionalni prijevod od strane stručnjaka. Ne preuzimamo odgovornost za bilo kakva nesporazuma ili pogrešna tumačenja koja mogu proizaći iz korištenja ovog prijevoda.\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-30T08:10:46+00:00",
"source_file": "lessons/5-NLP/16-RNN/RNNPyTorch.ipynb",
"language_code": "hr"
}
},
"nbformat": 4,
"nbformat_minor": 2
}

View File

@ -0,0 +1,460 @@
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Rekurentne neuronske mreže\n",
"\n",
"U prethodnom modulu obradili smo bogate semantičke reprezentacije teksta. Arhitektura koju smo koristili hvata agregirano značenje riječi u rečenici, ali ne uzima u obzir **redoslijed** riječi, jer operacija agregacije koja slijedi nakon ugrađivanja uklanja te informacije iz izvornog teksta. Budući da ti modeli ne mogu predstavljati redoslijed riječi, nisu sposobni riješiti složenije ili dvosmislene zadatke poput generiranja teksta ili odgovaranja na pitanja.\n",
"\n",
"Kako bismo uhvatili značenje sekvence teksta, koristit ćemo arhitekturu neuronske mreže zvanu **rekurentna neuronska mreža** ili RNN. Kada koristimo RNN, prosljeđujemo našu rečenicu kroz mrežu jedan token po jedan, a mreža proizvodi određeno **stanje**, koje zatim ponovno prosljeđujemo mreži s idućim tokenom.\n",
"\n",
"![Slika koja prikazuje primjer generiranja rekurentne neuronske mreže.](../../../../../translated_images/rnn.27f5c29c53d727b546ad3961637a267f0fe9ec5ab01f2a26a853c92fcefbb574.hr.png)\n",
"\n",
"S obzirom na ulaznu sekvencu tokena $X_0,\\dots,X_n$, RNN stvara sekvencu blokova neuronske mreže i trenira ovu sekvencu od početka do kraja koristeći unatrag širenje pogreške (backpropagation). Svaki blok mreže uzima par $(X_i,S_i)$ kao ulaz i proizvodi $S_{i+1}$ kao rezultat. Konačno stanje $S_n$ ili izlaz $Y_n$ ide u linearni klasifikator kako bi se proizveo rezultat. Svi blokovi mreže dijele iste težine i treniraju se od početka do kraja koristeći jedan prolaz unatrag širenja pogreške.\n",
"\n",
"> Gornja slika prikazuje rekurentnu neuronsku mrežu u razmotanom obliku (s lijeve strane) i u kompaktnijoj rekurentnoj reprezentaciji (s desne strane). Važno je shvatiti da sve RNN ćelije imaju iste **dijeljive težine**.\n",
"\n",
"Budući da se vektori stanja $S_0,\\dots,S_n$ prosljeđuju kroz mrežu, RNN može naučiti sekvencijalne ovisnosti između riječi. Na primjer, kada se riječ *ne* pojavi negdje u sekvenci, mreža može naučiti negirati određene elemente unutar vektora stanja.\n",
"\n",
"Unutar svake RNN ćelije nalaze se dvije matrice težina: $W_H$ i $W_I$, te pomak $b$. Na svakom koraku RNN-a, s obzirom na ulaz $X_i$ i ulazno stanje $S_i$, izlazno stanje se računa kao $S_{i+1} = f(W_H\\times S_i + W_I\\times X_i+b)$, gdje je $f$ funkcija aktivacije (često $\\tanh$).\n",
"\n",
"> Za probleme poput generiranja teksta (koje ćemo obraditi u sljedećoj jedinici) ili strojne prijevode također želimo dobiti neku izlaznu vrijednost na svakom koraku RNN-a. U tom slučaju postoji još jedna matrica $W_O$, a izlaz se računa kao $Y_i=f(W_O\\times S_i+b_O)$.\n",
"\n",
"Pogledajmo kako rekurentne neuronske mreže mogu pomoći u klasifikaciji našeg skupa podataka o vijestima.\n",
"\n",
"> Za okruženje sandboxa, potrebno je pokrenuti sljedeću ćeliju kako bismo osigurali da je potrebna biblioteka instalirana i da su podaci unaprijed dohvaćeni. Ako radite lokalno, možete preskočiti sljedeću ćeliju.\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": [
"Kod treniranja velikih modela, alokacija GPU memorije može postati problem. Također, možda ćemo trebati eksperimentirati s različitim veličinama minibatcha kako bi podaci stali u GPU memoriju, a istovremeno osigurali dovoljno brzu obuku. Ako pokrećete ovaj kod na vlastitom GPU računalu, možete eksperimentirati s prilagodbom veličine minibatcha kako biste ubrzali proces treniranja.\n",
"\n",
"> **Napomena**: Poznato je da određene verzije NVidia upravljačkih programa ne oslobađaju memoriju nakon treniranja modela. U ovom bilježniku pokrećemo nekoliko primjera, što može dovesti do iscrpljenja memorije u određenim konfiguracijama, posebno ako provodite vlastite eksperimente unutar istog bilježnika. Ako naiđete na neobične pogreške prilikom pokretanja treniranja modela, možda ćete morati ponovno pokrenuti kernel bilježnika.\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": [
"## Jednostavni RNN klasifikator\n",
"\n",
"U slučaju jednostavnog RNN-a, svaka rekurentna jedinica je jednostavna linearna mreža koja prima ulazni vektor i vektor stanja te proizvodi novi vektor stanja. U Kerasu, ovo se može predstaviti slojem `SimpleRNN`.\n",
"\n",
"Iako možemo proslijediti one-hot kodirane tokene izravno u RNN sloj, to nije dobra ideja zbog njihove visoke dimenzionalnosti. Stoga ćemo koristiti sloj za ugrađivanje (embedding) kako bismo smanjili dimenzionalnost vektora riječi, nakon čega slijedi RNN sloj, a na kraju `Dense` klasifikator.\n",
"\n",
"> **Napomena**: U slučajevima kada dimenzionalnost nije toliko visoka, na primjer kada se koristi tokenizacija na razini znakova, moglo bi imati smisla proslijediti one-hot kodirane tokene izravno u RNN ćeliju.\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": [
"> **Napomena:** Ovdje koristimo netrenirani sloj za ugrađivanje radi jednostavnosti, ali za bolje rezultate možemo koristiti unaprijed trenirani sloj za ugrađivanje koristeći Word2Vec, kako je opisano u prethodnoj jedinici. Bilo bi dobro vježbati prilagodbu ovog koda za rad s unaprijed treniranim ugrađivanjima.\n",
"\n",
"Sada ćemo trenirati naš RNN. Općenito, RNN-ove je prilično teško trenirati, jer kada se RNN ćelije razmota duž duljine sekvence, broj slojeva uključenih u unatrag širenje pogreške postaje vrlo velik. Zbog toga trebamo odabrati manju stopu učenja i trenirati mrežu na većem skupu podataka kako bismo postigli dobre rezultate. To može potrajati dosta dugo, pa je poželjno koristiti GPU.\n",
"\n",
"Kako bismo ubrzali proces, trenirat ćemo RNN model samo na naslovima vijesti, izostavljajući opis. Možete pokušati trenirati s opisom i vidjeti možete li postići da model uspješno trenira.\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": [
"> **Napomena** da je točnost vjerojatno niža ovdje, jer treniramo samo na naslovima vijesti.\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Ponovno razmatranje sekvenci varijabli\n",
"\n",
"Zapamtite da će sloj `TextVectorization` automatski popuniti sekvence varijabilne duljine u minibatchu s tokenima za popunjavanje. Ispostavlja se da ti tokeni također sudjeluju u treningu i mogu zakomplicirati konvergenciju modela.\n",
"\n",
"Postoji nekoliko pristupa koje možemo koristiti kako bismo smanjili količinu popunjavanja. Jedan od njih je preuređivanje skupa podataka prema duljini sekvence i grupiranje svih sekvenci prema veličini. To se može učiniti pomoću funkcije `tf.data.experimental.bucket_by_sequence_length` (pogledajte [dokumentaciju](https://www.tensorflow.org/api_docs/python/tf/data/experimental/bucket_by_sequence_length)).\n",
"\n",
"Drugi pristup je korištenje **maskiranja**. U Kerasu, neki slojevi podržavaju dodatni ulaz koji pokazuje koji tokeni trebaju biti uzeti u obzir tijekom treninga. Da bismo uključili maskiranje u naš model, možemo dodati zaseban sloj `Masking` ([dokumentacija](https://keras.io/api/layers/core_layers/masking/)), ili možemo postaviti parametar `mask_zero=True` u našem sloju `Embedding`.\n",
"\n",
"> **Note**: Ovaj trening će trajati oko 5 minuta za dovršavanje jedne epohe na cijelom skupu podataka. Slobodno prekinite trening u bilo kojem trenutku ako izgubite strpljenje. Također možete ograničiti količinu podataka korištenih za trening dodavanjem `.take(...)` klauzule nakon skupova podataka `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": [
"Sada kada koristimo maskiranje, možemo trenirati model na cijelom skupu podataka naslova i opisa.\n",
"\n",
"> **Note**: Jeste li primijetili da smo koristili vektorizator treniran na naslovima vijesti, a ne na cijelom tekstu članka? Potencijalno, to može uzrokovati da neki tokeni budu ignorirani, pa je bolje ponovno trenirati vektorizator. Međutim, to bi moglo imati samo vrlo mali učinak, pa ćemo se držati prethodno treniranog vektorizatora radi jednostavnosti.\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## LSTM: Dugoročna kratkoročna memorija\n",
"\n",
"Jedan od glavnih problema RNN-ova je **nestajanje gradijenata**. RNN-ovi mogu biti prilično dugi i mogu imati poteškoća s propagacijom gradijenata sve do prvog sloja mreže tijekom unatrag širenja. Kada se to dogodi, mreža ne može naučiti odnose između udaljenih tokena. Jedan od načina za izbjegavanje ovog problema je uvođenje **eksplicitnog upravljanja stanjem** pomoću **vrata**. Dvije najčešće arhitekture koje uvode vrata su **dugoročna kratkoročna memorija** (LSTM) i **jedinica s upravljanim prijenosom** (GRU). Ovdje ćemo obraditi LSTM-ove.\n",
"\n",
"![Slika koja prikazuje primjer ćelije dugoročne kratkoročne memorije](../../../../../lessons/5-NLP/16-RNN/images/long-short-term-memory-cell.svg)\n",
"\n",
"LSTM mreža je organizirana na način sličan RNN-u, ali postoje dva stanja koja se prenose iz sloja u sloj: stvarno stanje $c$ i skriveni vektor $h$. U svakoj jedinici, skriveni vektor $h_{t-1}$ kombinira se s ulazom $x_t$, i zajedno kontroliraju što se događa sa stanjem $c_t$ i izlazom $h_{t}$ putem **vrata**. Svaka vrata imaju sigmoidnu aktivaciju (izlaz u rasponu $[0,1]$), koja se može smatrati maskom po bitovima kada se pomnoži s vektorom stanja. LSTM-ovi imaju sljedeća vrata (s lijeva na desno na slici iznad):\n",
"* **vrata za zaborav** koja određuju koje komponente vektora $c_{t-1}$ trebamo zaboraviti, a koje proslijediti dalje.\n",
"* **ulazna vrata** koja određuju koliko informacija iz ulaznog vektora i prethodnog skrivenog vektora treba biti uključeno u vektor stanja.\n",
"* **izlazna vrata** koja uzimaju novi vektor stanja i odlučuju koji će njegovi dijelovi biti korišteni za stvaranje novog skrivenog vektora $h_t$.\n",
"\n",
"Komponente stanja $c$ mogu se smatrati zastavicama koje se mogu uključiti i isključiti. Na primjer, kada naiđemo na ime *Alice* u nizu, pretpostavljamo da se odnosi na ženu i podižemo zastavicu u stanju koja kaže da imamo imenicu ženskog roda u rečenici. Kada dalje naiđemo na riječi *i Tom*, podići ćemo zastavicu koja kaže da imamo množinsku imenicu. Tako, manipulirajući stanjem, možemo pratiti gramatička svojstva rečenice.\n",
"\n",
"> **Napomena**: Evo izvrsnog resursa za razumijevanje unutarnje strukture LSTM-ova: [Understanding LSTM Networks](https://colah.github.io/posts/2015-08-Understanding-LSTMs/) autora Christophera Olaha.\n",
"\n",
"Iako unutarnja struktura LSTM ćelije može izgledati složeno, Keras skriva ovu implementaciju unutar sloja `LSTM`, tako da je jedino što trebamo učiniti u gornjem primjeru zamijeniti rekurentni sloj:\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": [
"## Dvosmjerni i višeslojni RNN-ovi\n",
"\n",
"U našim dosadašnjim primjerima, rekurentne mreže obrađuju sekvencu od početka do kraja. To nam se čini prirodnim jer slijedi isti smjer kojim čitamo ili slušamo govor. Međutim, za scenarije koji zahtijevaju nasumičan pristup ulaznoj sekvenci, logičnije je pokrenuti rekurentne izračune u oba smjera. RNN-ovi koji omogućuju izračune u oba smjera nazivaju se **dvosmjerni** RNN-ovi, a mogu se stvoriti omatanjem rekurentnog sloja posebnim slojem `Bidirectional`.\n",
"\n",
"> **Napomena**: Sloj `Bidirectional` stvara dvije kopije sloja unutar sebe i postavlja svojstvo `go_backwards` jedne od tih kopija na `True`, čime omogućuje da ide u suprotnom smjeru duž sekvence.\n",
"\n",
"Rekurentne mreže, bilo jednosmjerne ili dvosmjerne, prepoznaju uzorke unutar sekvence i pohranjuju ih u vektore stanja ili ih vraćaju kao izlaz. Kao i kod konvolucijskih mreža, možemo izgraditi još jedan rekurentni sloj nakon prvog kako bismo prepoznali uzorke višeg nivoa, izgrađene na temelju uzoraka nižeg nivoa koje je izdvojio prvi sloj. To nas dovodi do pojma **višeslojnog RNN-a**, koji se sastoji od dvije ili više rekurentnih mreža, gdje se izlaz prethodnog sloja prosljeđuje sljedećem sloju kao ulaz.\n",
"\n",
"![Slika koja prikazuje višeslojni LSTM RNN](../../../../../translated_images/multi-layer-lstm.dd975e29bb2a59fe58b429db833932d734c81f211cad2783797a9608984acb8c.hr.jpg)\n",
"\n",
"*Slika preuzeta iz [ovog izvrsnog članka](https://towardsdatascience.com/from-a-lstm-cell-to-a-multilayer-lstm-network-with-pytorch-2899eb5696f3) autora Fernanda Lópeza.*\n",
"\n",
"Keras olakšava izgradnju ovih mreža jer je dovoljno dodati više rekurentnih slojeva u model. Za sve slojeve osim posljednjeg, potrebno je postaviti parametar `return_sequences=True`, jer želimo da sloj vraća sva međustanja, a ne samo konačno stanje rekurentnog izračuna.\n",
"\n",
"Izgradimo dvoslojni dvosmjerni LSTM za naš problem klasifikacije.\n",
"\n",
"> **Napomena** ovaj kod ponovno traje prilično dugo za izvršavanje, ali pruža najveću točnost koju smo dosad vidjeli. Možda se isplati pričekati i vidjeti rezultat.\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-ovi za druge zadatke\n",
"\n",
"Do sada smo se fokusirali na korištenje RNN-ova za klasifikaciju tekstualnih sekvenci. Međutim, oni mogu obraditi mnogo više zadataka, poput generiranja teksta i strojnog prevođenja — te ćemo zadatke razmotriti u sljedećoj jedinici.\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"\n---\n\n**Odricanje od odgovornosti**: \nOvaj dokument je preveden korištenjem AI usluge za prevođenje [Co-op Translator](https://github.com/Azure/co-op-translator). Iako nastojimo osigurati točnost, imajte na umu da automatski prijevodi mogu sadržavati pogreške ili netočnosti. Izvorni dokument na izvornom jeziku treba smatrati mjerodavnim izvorom. Za ključne informacije preporučuje se profesionalni prijevod od strane stručnjaka. Ne preuzimamo odgovornost za bilo kakve nesporazume ili pogrešne interpretacije proizašle iz korištenja ovog prijevoda.\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-30T08:09:04+00:00",
"source_file": "lessons/5-NLP/16-RNN/RNNTF.ipynb",
"language_code": "hr"
}
},
"nbformat": 4,
"nbformat_minor": 4
}

View File

@ -0,0 +1,411 @@
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Generativne mreže\n",
"\n",
"Rekurentne neuronske mreže (RNN-ovi) i njihove varijante s kontroliranim ćelijama, poput ćelija s dugoročnim i kratkoročnim pamćenjem (LSTM-ovi) i jedinica s kontroliranim povratnim signalom (GRU-ovi), omogućile su modeliranje jezika, tj. mogu naučiti redoslijed riječi i pružiti predviđanja za sljedeću riječ u nizu. To nam omogućuje korištenje RNN-ova za **generativne zadatke**, poput običnog generiranja teksta, strojnog prevođenja, pa čak i opisivanja slika.\n",
"\n",
"U RNN arhitekturi koju smo raspravili u prethodnoj jedinici, svaka RNN jedinica proizvodila je sljedeće skriveno stanje kao izlaz. Međutim, možemo dodati i drugi izlaz svakoj rekurentnoj jedinici, što bi nam omogućilo da dobijemo **niz** (koji je jednake duljine kao i izvorni niz). Štoviše, možemo koristiti RNN jedinice koje ne primaju ulaz na svakom koraku, već samo uzimaju neki početni vektor stanja i zatim proizvode niz izlaza.\n",
"\n",
"U ovom ćemo bilježniku fokus staviti na jednostavne generativne modele koji nam pomažu generirati tekst. Radi jednostavnosti, izgradit ćemo **mrežu na razini znakova**, koja generira tekst slovo po slovo. Tijekom treniranja, potrebno je uzeti neki korpus teksta i podijeliti ga na nizove slova.\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": [
"## Izgradnja vokabulara znakova\n",
"\n",
"Za izgradnju generativne mreže na razini znakova, potrebno je podijeliti tekst na pojedinačne znakove umjesto na riječi. Ovo se može postići definiranjem drugačijeg tokenizatora:\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": [
"Pogledajmo primjer kako možemo kodirati tekst iz našeg skupa podataka:\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": [
"## Treniranje generativne RNN\n",
"\n",
"Način na koji ćemo trenirati RNN za generiranje teksta je sljedeći. U svakom koraku uzet ćemo niz znakova duljine `nchars` i tražiti od mreže da generira sljedeći izlazni znak za svaki ulazni znak:\n",
"\n",
"![Slika koja prikazuje primjer RNN generiranja riječi 'HELLO'.](../../../../../translated_images/rnn-generate.56c54afb52f9781d63a7c16ea9c1b86cb70e6e1eae6a742b56b7b37468576b17.hr.png)\n",
"\n",
"Ovisno o stvarnom scenariju, možda ćemo htjeti uključiti i neke posebne znakove, poput *kraj-sekvence* `<eos>`. U našem slučaju, želimo trenirati mrežu za beskonačno generiranje teksta, stoga ćemo fiksirati veličinu svakog niza na `nchars` tokena. Posljedično, svaki primjer za treniranje sastojat će se od `nchars` ulaza i `nchars` izlaza (što je ulazni niz pomaknut za jedan simbol ulijevo). Minibatch će se sastojati od nekoliko takvih nizova.\n",
"\n",
"Način na koji ćemo generirati minibatcheve je da uzmemo svaki tekst vijesti duljine `l` i iz njega generiramo sve moguće kombinacije ulaz-izlaz (bit će ih `l-nchars` takvih kombinacija). Te kombinacije činit će jedan minibatch, a veličina minibatcheva bit će različita u svakom koraku treniranja.\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": [
"Sada ćemo definirati generator mrežu. Ona može biti bazirana na bilo kojoj rekurentnoj ćeliji o kojoj smo raspravljali u prethodnoj jedinici (jednostavna, LSTM ili GRU). U našem primjeru koristit ćemo LSTM.\n",
"\n",
"Budući da mreža prima znakove kao ulaz, a veličina vokabulara je prilično mala, ne trebamo sloj za ugradnju (embedding layer); ulaz kodiran u one-hot formatu može izravno ići u LSTM ćeliju. Međutim, budući da prosljeđujemo brojeve znakova kao ulaz, moramo ih kodirati u one-hot formatu prije nego ih proslijedimo u LSTM. To se postiže pozivanjem funkcije `one_hot` tijekom `forward` prolaza. Izlazni enkoder bit će linearni sloj koji će pretvoriti skriveno stanje u izlaz kodiran u one-hot formatu.\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": [
"Tijekom treniranja želimo imati mogućnost uzorkovanja generiranog teksta. Da bismo to postigli, definirat ćemo funkciju `generate` koja će proizvesti izlazni niz duljine `size`, počevši od početnog niza `start`.\n",
"\n",
"Način na koji to funkcionira je sljedeći. Prvo ćemo proslijediti cijeli početni niz kroz mrežu i uzeti izlazno stanje `s` i sljedeći predviđeni znak `out`. Budući da je `out` kodiran kao one-hot, uzimamo `argmax` kako bismo dobili indeks znaka `nc` u vokabularu, a zatim koristimo `itos` kako bismo odredili stvarni znak i dodali ga u rezultirajući popis znakova `chars`. Ovaj proces generiranja jednog znaka ponavlja se `size` puta kako bi se generirao potreban broj znakova.\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": [
"Sad krenimo s treningom! Petlja za trening gotovo je ista kao u svim našim prethodnim primjerima, ali umjesto točnosti ispisujemo uzorkovani generirani tekst svakih 1000 epoha.\n",
"\n",
"Posebnu pažnju treba obratiti na način na koji računamo gubitak. Moramo izračunati gubitak koristeći jednoznačno kodirani izlaz `out` i očekivani tekst `text_out`, koji je popis indeksa znakova. Srećom, funkcija `cross_entropy` očekuje nenormalizirani izlaz mreže kao prvi argument, a broj klase kao drugi, što je upravo ono što imamo. Također automatski provodi prosjek preko veličine minibatcha.\n",
"\n",
"Trening također ograničavamo na `samples_to_train` uzoraka, kako ne bismo predugo čekali. Potičemo vas da eksperimentirate i pokušate s duljim treningom, moguće kroz nekoliko epoha (u tom slučaju trebali biste stvoriti dodatnu petlju oko ovog koda).\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": [
"Ovaj primjer već generira prilično dobar tekst, ali može se dodatno poboljšati na nekoliko načina:\n",
"* **Bolja generacija minibatchova**. Način na koji smo pripremili podatke za treniranje bio je generiranje jednog minibatcha iz jednog uzorka. To nije idealno, jer minibatchevi imaju različite veličine, a neki se čak ne mogu generirati jer je tekst manji od `nchars`. Osim toga, mali minibatchevi ne iskorištavaju GPU dovoljno učinkovito. Pametnije bi bilo uzeti jedan veliki dio teksta iz svih uzoraka, zatim generirati sve ulazno-izlazne parove, promiješati ih i generirati minibatcheve jednake veličine.\n",
"* **Višeslojni LSTM**. Ima smisla isprobati 2 ili 3 sloja LSTM ćelija. Kao što smo spomenuli u prethodnoj jedinici, svaki sloj LSTM-a izvlači određene obrasce iz teksta, a u slučaju generatora na razini znakova možemo očekivati da će niži LSTM sloj biti odgovoran za izvlačenje slogova, a viši slojevi - za riječi i kombinacije riječi. Ovo se jednostavno može implementirati prosljeđivanjem parametra broja slojeva konstruktoru LSTM-a.\n",
"* Također biste mogli eksperimentirati s **GRU jedinicama** i vidjeti koje daju bolje rezultate, kao i s **različitim veličinama skrivenih slojeva**. Prevelik skriveni sloj može dovesti do prekomjernog prilagođavanja (npr. mreža će naučiti točan tekst), dok premala veličina možda neće dati dobar rezultat.\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Generiranje mekog teksta i temperatura\n",
"\n",
"U prethodnoj definiciji `generate`, uvijek smo uzimali znak s najvećom vjerojatnošću kao sljedeći znak u generiranom tekstu. To je često rezultiralo time da se tekst \"vrtio\" između istih sekvenci znakova iznova i iznova, kao u ovom primjeru:\n",
"```\n",
"today of the second the company and a second the company ...\n",
"```\n",
"\n",
"Međutim, ako pogledamo raspodjelu vjerojatnosti za sljedeći znak, može se dogoditi da razlika između nekoliko najvećih vjerojatnosti nije velika, npr. jedan znak može imati vjerojatnost 0.2, drugi 0.19, itd. Na primjer, kada tražimo sljedeći znak u sekvenci '*play*', sljedeći znak može jednako dobro biti razmak ili **e** (kao u riječi *player*).\n",
"\n",
"To nas dovodi do zaključka da nije uvijek \"pravedno\" odabrati znak s većom vjerojatnošću, jer odabir drugog po redu također može dovesti do smislenog teksta. Mudrije je **uzorkovati** znakove iz raspodjele vjerojatnosti koju daje izlaz mreže.\n",
"\n",
"Ovo uzorkovanje može se provesti pomoću funkcije `multinomial` koja implementira tzv. **multinomialnu raspodjelu**. Funkcija koja implementira ovo **meko** generiranje teksta definirana je u nastavku:\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": [
"Uveli smo još jedan parametar nazvan **temperature**, koji se koristi za označavanje koliko čvrsto trebamo slijediti najveću vjerojatnost. Ako je temperatura 1.0, radimo pošteno multinomijalno uzorkovanje, a kada temperatura ide prema beskonačnosti - sve vjerojatnosti postaju jednake i nasumično biramo sljedeći znak. U primjeru ispod možemo primijetiti da tekst postaje besmislen kada previše povećamo temperaturu, a nalikuje \"cikliranom\" teško generiranom tekstu kada se približi 0.\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"\n---\n\n**Odricanje od odgovornosti**: \nOvaj dokument je preveden korištenjem AI usluge za prevođenje [Co-op Translator](https://github.com/Azure/co-op-translator). Iako nastojimo osigurati točnost, imajte na umu da automatski prijevodi mogu sadržavati pogreške ili netočnosti. Izvorni dokument na izvornom jeziku treba smatrati mjerodavnim izvorom. Za ključne informacije preporučuje se profesionalni prijevod od strane stručnjaka. Ne preuzimamo odgovornost za bilo kakve nesporazume ili pogrešne interpretacije proizašle iz korištenja ovog prijevoda.\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-30T08:00:17+00:00",
"source_file": "lessons/5-NLP/17-GenerativeNetworks/GenerativePyTorch.ipynb",
"language_code": "hr"
}
},
"nbformat": 4,
"nbformat_minor": 4
}

View File

@ -0,0 +1,497 @@
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Generativne mreže\n",
"\n",
"Rekurentne neuronske mreže (RNN-ovi) i njihove varijante s kontroliranim ćelijama, poput ćelija dugog kratkoročnog pamćenja (LSTM-ova) i kontroliranih rekurentnih jedinica (GRU-ova), omogućile su modeliranje jezika, tj. mogu naučiti redoslijed riječi i pružiti predviđanja za sljedeću riječ u nizu. To nam omogućuje korištenje RNN-ova za **generativne zadatke**, poput običnog generiranja teksta, strojnog prevođenja, pa čak i opisivanja slika.\n",
"\n",
"U RNN arhitekturi koju smo raspravili u prethodnoj jedinici, svaka RNN jedinica proizvodila je sljedeće skriveno stanje kao izlaz. Međutim, možemo dodati i drugi izlaz svakoj rekurentnoj jedinici, što bi nam omogućilo da dobijemo **niz** (koji je jednake duljine kao i izvorni niz). Štoviše, možemo koristiti RNN jedinice koje ne primaju ulaz na svakom koraku, već samo uzimaju neki početni vektor stanja i zatim proizvode niz izlaza.\n",
"\n",
"U ovom ćemo se bilježniku usredotočiti na jednostavne generativne modele koji nam pomažu generirati tekst. Radi jednostavnosti, izgradimo **mrežu na razini znakova**, koja generira tekst slovo po slovo. Tijekom treniranja, trebamo uzeti neki korpus teksta i podijeliti ga na nizove slova.\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": [
"## Izgradnja vokabulara znakova\n",
"\n",
"Za izgradnju generativne mreže na razini znakova, potrebno je tekst podijeliti na pojedinačne znakove umjesto na riječi. `TextVectorization` sloj koji smo koristili ranije ne može to učiniti, pa imamo dvije opcije:\n",
"\n",
"* Ručno učitati tekst i izvršiti tokenizaciju 'ručno', kao u [ovom službenom Keras primjeru](https://keras.io/examples/generative/lstm_character_level_text_generation/)\n",
"* Koristiti `Tokenizer` klasu za tokenizaciju na razini znakova.\n",
"\n",
"Odlučit ćemo se za drugu opciju. `Tokenizer` se također može koristiti za tokenizaciju na razini riječi, pa bi trebalo biti jednostavno prebaciti se s tokenizacije na razini znakova na tokenizaciju na razini riječi.\n",
"\n",
"Za tokenizaciju na razini znakova, potrebno je proslijediti parametar `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": [
"Također želimo koristiti jedan poseban token za označavanje **kraja niza**, koji ćemo nazvati `<eos>`. Dodajmo ga ručno u vokabular:\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": [
"Sada, za kodiranje teksta u nizove brojeva, možemo koristiti:\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": [
"## Treniranje generativne RNN za generiranje naslova\n",
"\n",
"Način na koji ćemo trenirati RNN za generiranje naslova vijesti je sljedeći. U svakom koraku uzet ćemo jedan naslov, koji će se proslijediti u RNN, i za svaki ulazni znak tražit ćemo od mreže da generira sljedeći izlazni znak:\n",
"\n",
"![Slika koja prikazuje primjer generiranja riječi 'HELLO' pomoću RNN-a.](../../../../../translated_images/rnn-generate.56c54afb52f9781d63a7c16ea9c1b86cb70e6e1eae6a742b56b7b37468576b17.hr.png)\n",
"\n",
"Za posljednji znak našeg niza tražit ćemo od mreže da generira `<eos>` token.\n",
"\n",
"Glavna razlika generativne RNN koju ovdje koristimo je ta što ćemo uzimati izlaz iz svakog koraka RNN-a, a ne samo iz završne ćelije. To se može postići postavljanjem parametra `return_sequences` za RNN ćeliju.\n",
"\n",
"Dakle, tijekom treniranja, ulaz u mrežu bit će niz kodiranih znakova određene duljine, a izlaz će biti niz iste duljine, ali pomaknut za jedan element i završen s `<eos>`. Minibatch će se sastojati od nekoliko takvih nizova, a za poravnanje svih nizova trebat ćemo koristiti **padding**.\n",
"\n",
"Napravimo funkcije koje će transformirati skup podataka za nas. Budući da želimo dodati padding na razini minibatcha, prvo ćemo grupirati skup podataka pozivom `.batch()`, a zatim ga `map`-irati kako bismo izvršili transformaciju. Dakle, funkcija transformacije uzimat će cijeli minibatch kao parametar:\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": [
"Nekoliko važnih stvari koje ovdje radimo:\n",
"* Prvo izdvajamo stvarni tekst iz string tenzora\n",
"* `text_to_sequences` pretvara popis stringova u popis tenzora s cijelim brojevima\n",
"* `pad_sequences` dopunjava te tenzore do njihove maksimalne duljine\n",
"* Na kraju vršimo one-hot enkodiranje svih znakova, kao i pomicanje i dodavanje `<eos>`. Uskoro ćemo vidjeti zašto su nam potrebni one-hot enkodirani znakovi\n",
"\n",
"Međutim, ova funkcija je **Pythonic**, tj. ne može se automatski prevesti u Tensorflow računalni graf. Dobit ćemo pogreške ako pokušamo koristiti ovu funkciju izravno u funkciji `Dataset.map`. Moramo ovu Pythonic funkciju obuhvatiti koristeći `py_function` omotač:\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": [
"> **Napomena**: Razlikovanje između Pythonovih i Tensorflow funkcija za transformaciju podataka može se činiti prilično složenim, i možda se pitate zašto ne transformiramo skup podataka koristeći standardne Python funkcije prije nego ga proslijedimo u `fit`. Iako se to definitivno može učiniti, korištenje `Dataset.map` ima veliku prednost, jer se pipeline za transformaciju podataka izvršava koristeći Tensorflow-ov računalni graf, što omogućuje korištenje GPU-a za izračune i minimizira potrebu za prijenosom podataka između CPU-a i GPU-a.\n",
"\n",
"Sada možemo izgraditi naš generator mreže i započeti treniranje. Može se temeljiti na bilo kojoj rekurentnoj ćeliji koju smo raspravili u prethodnoj jedinici (jednostavna, LSTM ili GRU). U našem primjeru koristit ćemo LSTM.\n",
"\n",
"Budući da mreža prima znakove kao ulaz, a veličina vokabulara je prilično mala, ne trebamo sloj za ugrađivanje; ulaz kodiran u one-hot formatu može direktno ući u LSTM ćeliju. Izlazni sloj bit će `Dense` klasifikator koji će pretvoriti LSTM izlaz u brojeve tokena kodirane u one-hot formatu.\n",
"\n",
"Osim toga, budući da radimo s nizovima promjenjive duljine, možemo koristiti sloj `Masking` za stvaranje maske koja će ignorirati popunjeni dio niza. Ovo nije strogo potrebno, jer nas ne zanima previše sve što dolazi nakon `<eos>` tokena, ali ćemo ga koristiti radi stjecanja iskustva s ovom vrstom sloja. `input_shape` će biti `(None, vocab_size)`, gdje `None` označava niz promjenjive duljine, a izlazni oblik također je `(None, vocab_size)`, kao što možete vidjeti iz `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": [
"## Generiranje izlaza\n",
"\n",
"Sada kada smo model istrenirali, želimo ga koristiti za generiranje izlaza. Prije svega, trebamo način za dekodiranje teksta predstavljenog nizom brojeva tokena. Za to bismo mogli koristiti funkciju `tokenizer.sequences_to_texts`; međutim, ona ne funkcionira dobro s tokenizacijom na razini znakova. Stoga ćemo uzeti rječnik tokena iz tokenizatora (nazvan `word_index`), izgraditi obrnuti mapiranje i napisati vlastitu funkciju za dekodiranje:\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": [
"Sada ćemo započeti generiranje. Počet ćemo s nekim nizom `start`, kodirati ga u sekvencu `inp`, a zatim ćemo u svakom koraku pozvati našu mrežu kako bismo odredili sljedeći znak.\n",
"\n",
"Izlaz mreže `out` je vektor s `vocab_size` elemenata koji predstavljaju vjerojatnosti svakog tokena, a najvjerojatniji broj tokena možemo pronaći koristeći `argmax`. Zatim dodajemo ovaj znak generiranom popisu tokena i nastavljamo s generiranjem. Ovaj proces generiranja jednog znaka ponavlja se `size` puta kako bismo generirali potreban broj znakova, a završavamo ranije ako se naiđe na `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": [
"## Uzorkovanje rezultata tijekom treniranja\n",
"\n",
"Budući da nemamo korisne metrike poput *točnosti*, jedini način na koji možemo vidjeti da naš model postaje bolji jest **uzorkovanjem** generiranog niza tijekom treniranja. Da bismo to učinili, koristit ćemo **povratne pozive** (callbacks), tj. funkcije koje možemo proslijediti funkciji `fit`, a koje će se periodično pozivati tijekom treniranja.\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": [
"Ovaj primjer već generira prilično dobar tekst, ali može se dodatno poboljšati na nekoliko načina:\n",
"\n",
"* **Više teksta**. Koristili smo samo naslove za naš zadatak, ali možda biste htjeli eksperimentirati s punim tekstom. Imajte na umu da RNN-ovi nisu previše dobri u rukovanju dugim sekvencama, pa ima smisla ili ih podijeliti na kraće rečenice, ili uvijek trenirati na fiksnoj duljini sekvence neke unaprijed definirane vrijednosti `num_chars` (recimo, 256). Možete pokušati promijeniti gornji primjer u takvu arhitekturu, koristeći [službeni Keras vodič](https://keras.io/examples/generative/lstm_character_level_text_generation/) kao inspiraciju.\n",
"\n",
"* **Višeslojni LSTM**. Ima smisla isprobati 2 ili 3 sloja LSTM ćelija. Kao što smo spomenuli u prethodnoj jedinici, svaki sloj LSTM-a izdvaja određene uzorke iz teksta, a u slučaju generatora na razini znakova možemo očekivati da će niži LSTM sloj biti odgovoran za izdvajanje slogova, a viši slojevi - za riječi i kombinacije riječi. Ovo se jednostavno može implementirati prosljeđivanjem parametra broja slojeva konstruktoru LSTM-a.\n",
"\n",
"* Također biste mogli eksperimentirati s **GRU jedinicama** i vidjeti koje bolje funkcioniraju, kao i s **različitim veličinama skrivenih slojeva**. Prevelik skriveni sloj može rezultirati prekomjernim učenjem (npr. mreža će naučiti točan tekst), dok manja veličina možda neće dati dobar rezultat.\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Generiranje mekog teksta i temperatura\n",
"\n",
"U prethodnoj definiciji funkcije `generate`, uvijek smo birali znak s najvećom vjerojatnošću kao sljedeći znak u generiranom tekstu. To je često rezultiralo time da se tekst \"vrtio\" između istih sekvenci znakova iznova i iznova, kao u ovom primjeru:\n",
"```\n",
"today of the second the company and a second the company ...\n",
"```\n",
"\n",
"Međutim, ako pogledamo raspodjelu vjerojatnosti za sljedeći znak, može se dogoditi da razlika između nekoliko najvećih vjerojatnosti nije velika, npr. jedan znak može imati vjerojatnost 0.2, dok drugi ima 0.19, itd. Na primjer, kada tražimo sljedeći znak u sekvenci '*play*', sljedeći znak može jednako dobro biti razmak ili **e** (kao u riječi *player*).\n",
"\n",
"To nas dovodi do zaključka da nije uvijek \"pravedno\" odabrati znak s većom vjerojatnošću, jer odabir drugog po redu također može dovesti do smislenog teksta. Mudrije je **uzorkovati** znakove iz raspodjele vjerojatnosti koju daje izlaz mreže.\n",
"\n",
"Ovo uzorkovanje može se provesti pomoću funkcije `np.multinomial`, koja implementira tzv. **multinomialnu raspodjelu**. Funkcija koja implementira ovo **meko** generiranje teksta definirana je u nastavku:\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": [
"Uveli smo još jedan parametar nazvan **temperature** koji se koristi za označavanje koliko strogo trebamo slijediti najveću vjerojatnost. Ako je temperatura 1.0, provodimo pošteno multinomijalno uzorkovanje, a kada temperatura ide prema beskonačnosti - sve vjerojatnosti postaju jednake i nasumično biramo sljedeći znak. U primjeru ispod možemo primijetiti da tekst postaje besmislen kada previše povećamo temperaturu, a nalikuje \"cikliranom\" strogo generiranom tekstu kada se približi 0.\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"\n---\n\n**Odricanje od odgovornosti**: \nOvaj dokument je preveden korištenjem AI usluge za prevođenje [Co-op Translator](https://github.com/Azure/co-op-translator). Iako nastojimo osigurati točnost, imajte na umu da automatski prijevodi mogu sadržavati pogreške ili netočnosti. Izvorni dokument na izvornom jeziku treba smatrati mjerodavnim izvorom. Za ključne informacije preporučuje se profesionalni prijevod od strane stručnjaka. Ne preuzimamo odgovornost za bilo kakva nesporazuma ili pogrešna tumačenja koja mogu proizaći iz korištenja ovog prijevoda.\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-30T07:58:46+00:00",
"source_file": "lessons/5-NLP/17-GenerativeNetworks/GenerativeTF.ipynb",
"language_code": "hr"
}
},
"nbformat": 4,
"nbformat_minor": 4
}

View File

@ -0,0 +1,353 @@
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Mehanizmi pažnje i transformeri\n",
"\n",
"Jedan od glavnih nedostataka rekurentnih mreža je taj što sve riječi u nizu imaju isti utjecaj na rezultat. To uzrokuje suboptimalne performanse kod standardnih LSTM encoder-decoder modela za zadatke prevođenja nizova, poput prepoznavanja imenovanih entiteta i strojnog prevođenja. U stvarnosti, određene riječi u ulaznom nizu često imaju veći utjecaj na izlazne nizove od drugih.\n",
"\n",
"Razmotrimo model prevođenja nizova, poput strojnog prevođenja. On se implementira pomoću dvije rekurentne mreže, gdje jedna mreža (**encoder**) sažima ulazni niz u skriveno stanje, a druga mreža, **decoder**, razvija to skriveno stanje u prevedeni rezultat. Problem s ovim pristupom je što završno stanje mreže teško pamti početak rečenice, što uzrokuje lošu kvalitetu modela kod dugih rečenica.\n",
"\n",
"**Mehanizmi pažnje** omogućuju ponderiranje kontekstualnog utjecaja svakog ulaznog vektora na svaku izlaznu predikciju RNN-a. To se implementira stvaranjem prečaca između međustanja ulaznog RNN-a i izlaznog RNN-a. Na taj način, prilikom generiranja izlaznog simbola $y_t$, uzimamo u obzir sva skrivena stanja ulaza $h_i$, s različitim težinskim koeficijentima $\\alpha_{t,i}$.\n",
"\n",
"![Slika koja prikazuje encoder/decoder model s aditivnim slojem pažnje](../../../../../translated_images/encoder-decoder-attention.7a726296894fb567aa2898c94b17b3289087f6705c11907df8301df9e5eeb3de.hr.png)\n",
"*Encoder-decoder model s mehanizmom aditivne pažnje iz [Bahdanau et al., 2015](https://arxiv.org/pdf/1409.0473.pdf), citirano iz [ovog blog posta](https://lilianweng.github.io/lil-log/2018/06/24/attention-attention.html)*\n",
"\n",
"Matrica pažnje $\\{\\alpha_{i,j}\\}$ predstavlja stupanj u kojem određene ulazne riječi sudjeluju u generiranju određene riječi u izlaznom nizu. Ispod je primjer takve matrice:\n",
"\n",
"![Slika koja prikazuje uzorak poravnanja pronađen od strane RNNsearch-50, preuzeto iz Bahdanau - arviz.org](../../../../../translated_images/bahdanau-fig3.09ba2d37f202a6af11de6c82d2d197830ba5f4528d9ea430eb65fd3a75065973.hr.png)\n",
"\n",
"*Slika preuzeta iz [Bahdanau et al., 2015](https://arxiv.org/pdf/1409.0473.pdf) (Slika 3)*\n",
"\n",
"Mehanizmi pažnje odgovorni su za velik dio trenutnog ili gotovo trenutnog stanja tehnike u obradi prirodnog jezika. Međutim, dodavanje pažnje značajno povećava broj parametara modela, što je dovelo do problema skaliranja s RNN-ovima. Ključno ograničenje skaliranja RNN-ova je to što rekurentna priroda modela otežava grupiranje i paralelizaciju treninga. U RNN-u svaki element niza mora se obraditi redoslijedom, što znači da se ne može lako paralelizirati.\n",
"\n",
"Usvajanje mehanizama pažnje u kombinaciji s ovim ograničenjem dovelo je do stvaranja sadašnjih modela transformera, koji su trenutno stanje tehnike, a koje danas poznajemo i koristimo, od BERT-a do OpenGPT3.\n",
"\n",
"## Modeli transformera\n",
"\n",
"Umjesto prosljeđivanja konteksta svake prethodne predikcije u sljedeći korak evaluacije, **modeli transformera** koriste **pozicijske kodove** i pažnju kako bi uhvatili kontekst danog ulaza unutar zadanog prozora teksta. Slika ispod prikazuje kako pozicijski kodovi s pažnjom mogu uhvatiti kontekst unutar zadanog prozora.\n",
"\n",
"![Animirani GIF koji prikazuje kako se evaluacije provode u modelima transformera.](../../../../../lessons/5-NLP/18-Transformers/images/transformer-animated-explanation.gif)\n",
"\n",
"Budući da se svaka ulazna pozicija neovisno mapira na svaku izlaznu poziciju, transformeri se mogu bolje paralelizirati od RNN-ova, što omogućuje mnogo veće i izražajnije jezične modele. Svaka glava pažnje može se koristiti za učenje različitih odnosa između riječi, što poboljšava zadatke obrade prirodnog jezika.\n",
"\n",
"**BERT** (Bidirectional Encoder Representations from Transformers) je vrlo velika višeslojna mreža transformera s 12 slojeva za *BERT-base* i 24 za *BERT-large*. Model se prvo unaprijed trenira na velikom korpusu tekstualnih podataka (WikiPedia + knjige) koristeći nenadzirano učenje (predviđanje maskiranih riječi u rečenici). Tijekom unaprijed treniranja model usvaja značajnu razinu razumijevanja jezika, koja se zatim može iskoristiti s drugim skupovima podataka pomoću finog podešavanja. Ovaj proces naziva se **prijenosno učenje**.\n",
"\n",
"![Slika s http://jalammar.github.io/illustrated-bert/](../../../../../translated_images/jalammarBERT-language-modeling-masked-lm.34f113ea5fec4362e39ee4381aab7cad06b5465a0b5f053a0f2aa05fbe14e746.hr.png)\n",
"\n",
"Postoji mnogo varijacija arhitektura transformera, uključujući BERT, DistilBERT, BigBird, OpenGPT3 i druge, koje se mogu fino podešavati. Paket [HuggingFace](https://github.com/huggingface/) pruža repozitorij za treniranje mnogih od ovih arhitektura s PyTorchom.\n",
"\n",
"## Korištenje BERT-a za klasifikaciju teksta\n",
"\n",
"Pogledajmo kako možemo koristiti unaprijed trenirani BERT model za rješavanje našeg tradicionalnog zadatka: klasifikacije nizova. Klasificirat ćemo naš izvorni AG News skup podataka.\n",
"\n",
"Prvo, učitajmo HuggingFace biblioteku i naš skup podataka:\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": [
"Budući da ćemo koristiti unaprijed trenirani BERT model, trebamo koristiti specifičan tokenizer. Prvo ćemo učitati tokenizer povezan s unaprijed treniranim BERT modelom.\n",
"\n",
"Biblioteka HuggingFace sadrži repozitorij unaprijed treniranih modela, koje možete koristiti jednostavno tako da navedete njihova imena kao argumente funkcijama `from_pretrained`. Svi potrebni binarni fajlovi za model automatski će se preuzeti.\n",
"\n",
"Međutim, ponekad ćete trebati učitati vlastite modele, u kojem slučaju možete navesti direktorij koji sadrži sve relevantne datoteke, uključujući parametre za tokenizer, datoteku `config.json` s parametrima modela, binarne težine itd.\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": [
"Objekt `tokenizer` sadrži funkciju `encode` koja se može izravno koristiti za kodiranje teksta:\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": [
"Zatim, kreirajmo iteratore koje ćemo koristiti tijekom treninga za pristup podacima. Budući da BERT koristi svoju vlastitu funkciju kodiranja, trebali bismo definirati funkciju za popunjavanje sličnu `padify` koju smo ranije definirali:\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": [
"U našem slučaju, koristit ćemo unaprijed istrenirani BERT model nazvan `bert-base-uncased`. Učitajmo model koristeći paket `BertForSequenceClassfication`. Ovo osigurava da naš model već ima potrebnu arhitekturu za klasifikaciju, uključujući završni klasifikator. Vidjet ćete poruku upozorenja koja navodi da težine završnog klasifikatora nisu inicijalizirane i da bi model zahtijevao prethodno treniranje - to je potpuno u redu, jer upravo to namjeravamo učiniti!\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": [
"Sada smo spremni započeti treniranje! Budući da je BERT već prethodno istreniran, želimo započeti s prilično malom stopom učenja kako ne bismo uništili početne težine.\n",
"\n",
"Sav težak posao obavlja model `BertForSequenceClassification`. Kada pozovemo model na podatke za treniranje, on vraća i gubitak (loss) i izlaz mreže za ulazni minibatch. Gubitak koristimo za optimizaciju parametara (`loss.backward()` provodi povratno širenje), a `out` koristimo za izračun točnosti treniranja uspoređujući dobivene oznake `labs` (izračunate pomoću `argmax`) s očekivanim `labels`.\n",
"\n",
"Kako bismo kontrolirali proces, akumuliramo gubitak i točnost kroz nekoliko iteracija te ih ispisujemo svakih `report_freq` ciklusa treniranja.\n",
"\n",
"Ovo treniranje će vjerojatno trajati dosta dugo, pa ograničavamo broj iteracija.\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": [
"Možete vidjeti (posebno ako povećate broj iteracija i pričekate dovoljno dugo) da BERT klasifikacija daje prilično dobru točnost! To je zato što BERT već prilično dobro razumije strukturu jezika, a mi samo trebamo fino podesiti završni klasifikator. Međutim, budući da je BERT veliki model, cijeli proces treniranja traje dugo i zahtijeva ozbiljnu računalnu snagu! (GPU, i po mogućnosti više od jednog).\n",
"\n",
"> **Note:** U našem primjeru koristili smo jedan od najmanjih unaprijed treniranih BERT modela. Postoje veći modeli koji vjerojatno daju bolje rezultate.\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Procjena performansi modela\n",
"\n",
"Sada možemo procijeniti performanse našeg modela na testnom skupu podataka. Petlja za evaluaciju je prilično slična petlji za treniranje, ali ne smijemo zaboraviti prebaciti model u način evaluacije pozivom `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": [
"## Ključne točke\n",
"\n",
"U ovoj jedinici vidjeli smo koliko je jednostavno uzeti unaprijed trenirani jezični model iz biblioteke **transformers** i prilagoditi ga našem zadatku klasifikacije teksta. Slično tome, BERT modeli mogu se koristiti za ekstrakciju entiteta, odgovaranje na pitanja i druge NLP zadatke.\n",
"\n",
"Transformers modeli predstavljaju trenutno najnaprednije stanje u NLP-u, i u većini slučajeva trebali bi biti prvo rješenje s kojim započinjete eksperimentiranje prilikom implementacije prilagođenih NLP rješenja. Međutim, razumijevanje osnovnih temeljnih principa rekurentnih neuronskih mreža, o kojima smo raspravljali u ovom modulu, iznimno je važno ako želite izgraditi napredne neuronske modele.\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"\n---\n\n**Odricanje od odgovornosti**: \nOvaj dokument je preveden pomoću AI usluge za prevođenje [Co-op Translator](https://github.com/Azure/co-op-translator). Iako nastojimo osigurati točnost, imajte na umu da automatski prijevodi mogu sadržavati pogreške ili netočnosti. Izvorni dokument na izvornom jeziku treba smatrati autoritativnim izvorom. Za ključne informacije preporučuje se profesionalni prijevod od strane ljudskog prevoditelja. Ne preuzimamo odgovornost za nesporazume ili pogrešne interpretacije koje mogu proizaći iz korištenja ovog prijevoda.\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-30T08:03:45+00:00",
"source_file": "lessons/5-NLP/18-Transformers/TransformersPyTorch.ipynb",
"language_code": "hr"
}
},
"nbformat": 4,
"nbformat_minor": 4
}

View File

@ -0,0 +1,825 @@
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Mehanizmi pažnje i transformeri\n",
"\n",
"Jedan od glavnih nedostataka rekurentnih mreža je taj što sve riječi u nizu imaju isti utjecaj na rezultat. To uzrokuje suboptimalne performanse kod standardnih LSTM encoder-decoder modela za zadatke prevođenja sekvenci, poput prepoznavanja imenovanih entiteta i strojnog prevođenja. U stvarnosti, određene riječi u ulaznom nizu često imaju veći utjecaj na izlazne sekvence od drugih.\n",
"\n",
"Razmotrimo model prevođenja sekvenci, poput strojnog prevođenja. On se implementira pomoću dvije rekurentne mreže, gdje jedna mreža (**encoder**) sažima ulazni niz u skriveno stanje, a druga mreža (**decoder**) razvija to skriveno stanje u prevedeni rezultat. Problem s ovim pristupom je što završno stanje mreže teško pamti početak rečenice, što uzrokuje lošu kvalitetu modela kod dugih rečenica.\n",
"\n",
"**Mehanizmi pažnje** omogućuju ponderiranje kontekstualnog utjecaja svakog ulaznog vektora na svaku izlaznu predikciju RNN-a. To se implementira stvaranjem prečaca između međustanja ulaznog RNN-a i izlaznog RNN-a. Na taj način, prilikom generiranja izlaznog simbola $y_t$, uzimamo u obzir sva ulazna skrivena stanja $h_i$, s različitim težinskim koeficijentima $\\alpha_{t,i}$.\n",
"\n",
"![Slika koja prikazuje encoder/decoder model s aditivnim slojem pažnje](../../../../../translated_images/encoder-decoder-attention.7a726296894fb567aa2898c94b17b3289087f6705c11907df8301df9e5eeb3de.hr.png)\n",
"*Encoder-decoder model s aditivnim mehanizmom pažnje u [Bahdanau et al., 2015](https://arxiv.org/pdf/1409.0473.pdf), citirano iz [ovog blog posta](https://lilianweng.github.io/lil-log/2018/06/24/attention-attention.html)*\n",
"\n",
"Matrica pažnje $\\{\\alpha_{i,j}\\}$ predstavlja stupanj u kojem određene ulazne riječi sudjeluju u generiranju određene riječi u izlaznom nizu. Ispod je primjer takve matrice:\n",
"\n",
"![Slika koja prikazuje uzorak poravnanja pronađenog pomoću RNNsearch-50, preuzeto iz Bahdanau - arviz.org](../../../../../translated_images/bahdanau-fig3.09ba2d37f202a6af11de6c82d2d197830ba5f4528d9ea430eb65fd3a75065973.hr.png)\n",
"\n",
"*Slika preuzeta iz [Bahdanau et al., 2015](https://arxiv.org/pdf/1409.0473.pdf) (Slika 3)*\n",
"\n",
"Mehanizmi pažnje odgovorni su za velik dio trenutnog ili gotovo trenutnog stanja umjetnosti u obradi prirodnog jezika. Međutim, dodavanje pažnje značajno povećava broj parametara modela, što je dovelo do problema skaliranja s RNN-ovima. Ključno ograničenje skaliranja RNN-ova je to što rekurentna priroda modela otežava grupiranje i paralelizaciju treninga. U RNN-u svaki element sekvence mora se obraditi redoslijedom, što znači da se ne može lako paralelizirati.\n",
"\n",
"Usvajanje mehanizama pažnje u kombinaciji s ovim ograničenjem dovelo je do stvaranja sadašnjih transformera, koji predstavljaju stanje umjetnosti, a koje danas poznajemo i koristimo, od BERT-a do OpenGPT3.\n",
"\n",
"## Transformer modeli\n",
"\n",
"Umjesto prosljeđivanja konteksta svake prethodne predikcije u sljedeći korak evaluacije, **transformer modeli** koriste **pozicijske kodove** i **pažnju** kako bi uhvatili kontekst danog ulaza unutar zadanog prozora teksta. Slika ispod prikazuje kako pozicijski kodovi s pažnjom mogu uhvatiti kontekst unutar zadanog prozora.\n",
"\n",
"![Animirani GIF koji prikazuje kako se evaluacije provode u transformer modelima.](../../../../../lessons/5-NLP/18-Transformers/images/transformer-animated-explanation.gif)\n",
"\n",
"Budući da se svaka ulazna pozicija neovisno mapira na svaku izlaznu poziciju, transformeri mogu bolje paralelizirati od RNN-ova, što omogućuje mnogo veće i izražajnije jezične modele. Svaka glava pažnje može se koristiti za učenje različitih odnosa između riječi, što poboljšava zadatke obrade prirodnog jezika.\n",
"\n",
"## Izgradnja jednostavnog transformer modela\n",
"\n",
"Keras ne sadrži ugrađeni sloj za transformere, ali možemo izgraditi vlastiti. Kao i prije, fokusirat ćemo se na klasifikaciju teksta AG News skupa podataka, no vrijedi napomenuti da transformer modeli pokazuju najbolje rezultate na složenijim zadacima obrade prirodnog jezika.\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": [
"Novi slojevi u Kerasi trebaju naslijediti klasu `Layer` i implementirati metodu `call`. Počnimo s **Positional Embedding** slojem. Koristit ćemo [neki kod iz službene Keras dokumentacije](https://keras.io/examples/nlp/text_classification_with_transformer/). Pretpostavit ćemo da sve ulazne sekvence popunjavamo do duljine `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": [
"Ovaj sloj sastoji se od dva sloja `Embedding`: za ugradnju tokena (na način koji smo ranije raspravili) i pozicija tokena. Pozicije tokena stvaraju se kao niz prirodnih brojeva od 0 do `maxlen` koristeći `tf.range`, a zatim se prosljeđuju kroz sloj za ugradnju. Dva dobivena vektora ugradnje se zatim zbrajaju, stvarajući pozicijski ugrađenu reprezentaciju ulaza oblika `maxlen`$\\times$`embed_dim`.\n",
"\n",
"Sada, implementirajmo transformer blok. On će uzeti izlaz prethodno definiranog sloja za ugradnju:\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 primjenjuje `MultiHeadAttention` na ulaz s pozicijskim kodiranjem kako bi proizveo vektor pažnje dimenzije `maxlen`$\\times$`embed_dim`, koji se zatim miješa s ulazom i normalizira pomoću `LayerNormalization`.\n",
"\n",
"> **Napomena**: `LayerNormalization` je sličan `BatchNormalization` koji je obrađen u dijelu *Računalni vid* ovog programa učenja, ali normalizira izlaze prethodnog sloja za svaki uzorak u treningu neovisno, kako bi ih doveo u raspon [-1..1].\n",
"\n",
"Izlaz ovog sloja zatim se prosljeđuje kroz `Dense` mrežu (u našem slučaju - perceptron s dva sloja), a rezultat se dodaje konačnom izlazu (koji se ponovno podvrgava normalizaciji).\n",
"\n",
"Sada smo spremni definirati kompletan model transformatora:\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 modeli\n",
"\n",
"**BERT** (Bidirectional Encoder Representations from Transformers) je vrlo velika višeslojna transformacijska mreža s 12 slojeva za *BERT-base* i 24 za *BERT-large*. Model se prvo unaprijed trenira na velikom korpusu tekstualnih podataka (WikiPedia + knjige) koristeći nenadzirano učenje (predviđanje maskiranih riječi u rečenici). Tijekom unaprijednog treniranja model usvaja značajnu razinu razumijevanja jezika, što se kasnije može iskoristiti s drugim skupovima podataka putem finog podešavanja. Ovaj proces naziva se **transferno učenje**.\n",
"\n",
"![slika s http://jalammar.github.io/illustrated-bert/](../../../../../translated_images/jalammarBERT-language-modeling-masked-lm.34f113ea5fec4362e39ee4381aab7cad06b5465a0b5f053a0f2aa05fbe14e746.hr.png)\n",
"\n",
"Postoji mnogo varijacija transformacijskih arhitektura, uključujući BERT, DistilBERT, BigBird, OpenGPT3 i druge, koje se mogu fino podešavati.\n",
"\n",
"Pogledajmo kako možemo koristiti unaprijed trenirani BERT model za rješavanje našeg tradicionalnog problema klasifikacije sekvenci. Posudit ćemo ideju i dio koda iz [službene dokumentacije](https://www.tensorflow.org/text/tutorials/classify_text_with_bert).\n",
"\n",
"Za učitavanje unaprijed treniranih modela koristit ćemo **Tensorflow hub**. Prvo, učitajmo BERT-specifični vektorizator:\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": [
"Važno je koristiti isti vektorizator koji je korišten za treniranje originalne mreže. Također, BERT vektorizator vraća tri komponente:\n",
"* `input_word_ids`, što je niz brojeva tokena za ulaznu rečenicu\n",
"* `input_mask`, koji pokazuje koji dio niza sadrži stvarni unos, a koji je popuna. Slično je maski koju proizvodi sloj `Masking`\n",
"* `input_type_ids` koristi se za zadatke modeliranja jezika i omogućuje specificiranje dvije ulazne rečenice u jednom nizu.\n",
"\n",
"Zatim možemo instancirati BERT ekstraktor značajki:\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": [
"Dakle, BERT sloj vraća nekoliko korisnih rezultata:\n",
"* `pooled_output` je rezultat prosjeka svih tokena u sekvenci. Možete ga smatrati inteligentnim semantičkim ugrađivanjem cijele mreže. Ekvivalentan je izlazu sloja `GlobalAveragePooling1D` u našem prethodnom modelu.\n",
"* `sequence_output` je izlaz posljednjeg transformacijskog sloja (odgovara izlazu `TransformerBlock` u našem modelu iznad).\n",
"* `encoder_outputs` su izlazi svih transformacijskih slojeva. Budući da smo učitali BERT model s 4 sloja (kao što vjerojatno možete zaključiti iz imena koje sadrži `4_H`), ima 4 tenzora. Posljednji je isti kao `sequence_output`.\n",
"\n",
"Sada ćemo definirati end-to-end model za klasifikaciju. Koristit ćemo *funkcionalnu definiciju modela*, gdje definiramo ulaz modela, a zatim pružamo niz izraza za izračun njegovog izlaza. Također ćemo postaviti težine BERT modela kao ne-trenirajuće i trenirati samo završni klasifikator:\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": [
"Unatoč tome što postoji malo parametara za treniranje, proces je prilično spor jer je BERT-ov ekstraktor značajki računalno zahtjevan. Čini se da nismo uspjeli postići zadovoljavajuću točnost, bilo zbog nedostatka treninga ili nedostatka parametara modela.\n",
"\n",
"Pokušajmo otključati težine BERT-a i trenirati ga također. Ovo zahtijeva vrlo malu stopu učenja, kao i pažljiviju strategiju treniranja s **zagrijavanjem** koristeći **AdamW** optimizator. Koristit ćemo paket `tf-models-official` za kreiranje optimizatora:\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": [
"Kao što možete vidjeti, treniranje ide prilično sporo - ali možda biste željeli eksperimentirati i trenirati model kroz nekoliko epoha (5-10) kako biste vidjeli možete li postići najbolji rezultat u usporedbi s pristupima koje smo koristili ranije.\n",
"\n",
"## Huggingface Transformers knjižnica\n",
"\n",
"Još jedan vrlo čest (i malo jednostavniji) način korištenja Transformer modela je [HuggingFace paket](https://github.com/huggingface/), koji pruža jednostavne građevne blokove za različite NLP zadatke. Dostupan je i za Tensorflow i za PyTorch, još jedan vrlo popularan okvir za neuronske mreže.\n",
"\n",
"> **Napomena**: Ako vas ne zanima kako radi Transformers knjižnica - možete preskočiti na kraj ovog bilježnika, jer nećete vidjeti ništa bitno drugačije od onoga što smo već radili. Ponavljat ćemo iste korake treniranja BERT modela koristeći drugu knjižnicu i znatno veći model. Stoga proces uključuje prilično dugo treniranje, pa možda želite samo pregledati kod.\n",
"\n",
"Pogledajmo kako se naš problem može riješiti koristeći [Huggingface Transformers](http://huggingface.co).\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Prvo što trebamo učiniti je odabrati model koji ćemo koristiti. Osim nekih ugrađenih modela, Huggingface sadrži [online repozitorij modela](https://huggingface.co/models), gdje možete pronaći mnogo više unaprijed treniranih modela koje je kreirala zajednica. Svi ti modeli mogu se učitati i koristiti jednostavnim navođenjem imena modela. Svi potrebni binarni datoteke za model automatski će se preuzeti.\n",
"\n",
"Ponekad ćete trebati učitati vlastite modele, u tom slučaju možete navesti direktorij koji sadrži sve relevantne datoteke, uključujući parametre za tokenizer, datoteku `config.json` s parametrima modela, binarne težine itd.\n",
"\n",
"Na temelju imena modela, možemo instancirati i model i tokenizer. Započnimo s tokenizerom:\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": [
"Objekt `tokenizer` sadrži funkciju `encode` koja se može izravno koristiti za kodiranje teksta:\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": [
"Također možemo koristiti tokenizer za kodiranje sekvence na način prikladan za prosljeđivanje modelu, tj. uključujući polja `token_ids`, `input_mask`, itd. Također možemo specificirati da želimo Tensorflow tenzore pružanjem argumenta `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": [
"U ovom slučaju, koristit ćemo unaprijed istrenirani BERT model nazvan `bert-base-uncased`. *Uncased* označava da model nije osjetljiv na velika i mala slova.\n",
"\n",
"Prilikom treniranja modela, potrebno je osigurati tokenizirani niz kao ulaz, i stoga ćemo osmisliti procesnu liniju za obradu podataka. Budući da je `tokenizer.encode` Python funkcija, koristit ćemo isti pristup kao u posljednjoj jedinici pozivajući je pomoću `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": [
"Sada možemo učitati stvarni model koristeći paket `BertForSequenceClassification`. Ovo osigurava da naš model već ima potrebnu arhitekturu za klasifikaciju, uključujući završni klasifikator. Vidjet ćete poruku upozorenja koja navodi da težine završnog klasifikatora nisu inicijalizirane i da bi model zahtijevao prethodnu obuku - to je potpuno u redu, jer upravo to namjeravamo učiniti!\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": [
"Kao što možete vidjeti iz `summary()`, model sadrži gotovo 110 milijuna parametara! Vjerojatno, ako želimo jednostavan zadatak klasifikacije na relativno malom skupu podataka, ne želimo trenirati osnovni BERT sloj:\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": [
"Sada smo spremni započeti treniranje!\n",
"\n",
"> **Napomena**: Treniranje BERT modela u punom opsegu može biti vrlo dugotrajno! Stoga ćemo ga trenirati samo za prvih 32 serije. Ovo je samo da pokažemo kako se postavlja treniranje modela. Ako ste zainteresirani za treniranje u punom opsegu - samo uklonite parametre `steps_per_epoch` i `validation_steps`, i pripremite se na čekanje!\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": [
"Ako povećate broj iteracija i pričekate dovoljno dugo, te trenirate kroz nekoliko epoha, možete očekivati da će BERT klasifikacija pružiti najbolju točnost! To je zato što BERT već prilično dobro razumije strukturu jezika, pa je potrebno samo fino prilagoditi završni klasifikator. Međutim, budući da je BERT veliki model, cijeli proces treniranja traje dugo i zahtijeva ozbiljnu računalnu snagu! (GPU, i po mogućnosti više od jednog).\n",
"\n",
"> **Napomena:** U našem primjeru koristili smo jedan od najmanjih unaprijed istreniranih BERT modela. Postoje veći modeli koji vjerojatno daju bolje rezultate.\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Ključne točke\n",
"\n",
"U ovoj jedinici smo se upoznali s vrlo recentnim arhitekturama modela temeljenim na **transformerima**. Primijenili smo ih za naš zadatak klasifikacije teksta, ali na sličan način BERT modeli mogu se koristiti za ekstrakciju entiteta, odgovaranje na pitanja i druge NLP zadatke.\n",
"\n",
"Transformerski modeli predstavljaju trenutno najnaprednije stanje u NLP-u, i u većini slučajeva trebali bi biti prvo rješenje s kojim započinjete eksperimentiranje prilikom implementacije prilagođenih NLP rješenja. Međutim, razumijevanje osnovnih principa rekurentnih neuronskih mreža, o kojima smo raspravljali u ovom modulu, izuzetno je važno ako želite izgraditi napredne neuronske modele.\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": []
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"\n---\n\n**Odricanje od odgovornosti**: \nOvaj dokument je preveden pomoću AI usluge za prevođenje [Co-op Translator](https://github.com/Azure/co-op-translator). Iako nastojimo osigurati točnost, imajte na umu da automatski prijevodi mogu sadržavati pogreške ili netočnosti. Izvorni dokument na izvornom jeziku treba smatrati autoritativnim izvorom. Za ključne informacije preporučuje se profesionalni prijevod od strane ljudskog prevoditelja. Ne preuzimamo odgovornost za bilo kakve nesporazume ili pogrešne interpretacije koje proizlaze iz korištenja ovog prijevoda.\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-30T08:05:38+00:00",
"source_file": "lessons/5-NLP/18-Transformers/TransformersTF.ipynb",
"language_code": "hr"
}
},
"nbformat": 4,
"nbformat_minor": 4
}

View File

@ -0,0 +1,492 @@
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Prepoznavanje imenovanih entiteta (NER)\n",
"\n",
"Ova bilježnica dolazi iz [AI for Beginners Curriculum](http://aka.ms/ai-beginners).\n",
"\n",
"U ovom primjeru naučit ćemo kako trenirati NER model na [Annotated Corpus for Named Entity Recognition](https://www.kaggle.com/datasets/abhinavwalia95/entity-annotated-corpus) skupu podataka s Kaggla. Prije nego što nastavimo, preuzmite datoteku [ner_dataset.csv](https://www.kaggle.com/datasets/abhinavwalia95/entity-annotated-corpus?resource=download&select=ner_dataset.csv) u trenutni direktorij.\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": [
"## Priprema skupa podataka\n",
"\n",
"Započet ćemo učitavanjem skupa podataka u dataframe. Ako želite saznati više o korištenju Pandasa, posjetite [lekciju o obradi podataka](https://github.com/microsoft/Data-Science-For-Beginners/tree/main/2-Working-With-Data/07-python) u našem [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": [
"Hajdemo dobiti jedinstvene oznake i stvoriti rječnike za pretraživanje koje možemo koristiti za pretvaranje oznaka u brojeve klasa:\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": [
"Sada trebamo učiniti isto s rječnikom. Radi jednostavnosti, stvorit ćemo rječnik bez uzimanja u obzir učestalosti riječi; u stvarnom životu možda biste željeli koristiti Keras vektorizator i ograničiti broj riječi.\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": [
"Moramo stvoriti skup podataka rečenica za treniranje. Prođimo kroz izvorni skup podataka i odvojimo sve pojedinačne rečenice u `X` (popise riječi) i `Y` (popis oznaka):\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": [
"Radi jednostavnosti, popunit ćemo sve rečenice s 0 tokena do maksimalne duljine. U stvarnom životu, možda bismo željeli koristiti pametniju strategiju i popunjavati sekvence samo unutar jedne mini serije.\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": [
"## Definiranje mreže za klasifikaciju tokena\n",
"\n",
"Koristit ćemo dvoslojnu dvosmjernu LSTM mrežu za klasifikaciju tokena. Kako bismo primijenili gusti klasifikator na svaki izlaz posljednjeg LSTM sloja, koristit ćemo konstrukciju `TimeDistributed`, koja replicira isti gusti sloj na svaki izlaz LSTM-a u svakom koraku:\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": [
"Imajte na umu da ovdje eksplicitno specificiramo `maxlen` za naš skup podataka - ako želimo da mreža može obrađivati sekvence promjenjive duljine, moramo biti malo domišljatiji prilikom definiranja mreže.\n",
"\n",
"Sada ćemo trenirati model. Radi brzine, trenirat ćemo samo jedan epoch, ali možete pokušati trenirati dulje. Također, možda biste željeli odvojiti dio skupa podataka kao skup za treniranje kako biste promatrali točnost validacije.\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": [
"## Testiranje rezultata\n",
"\n",
"Pogledajmo sada kako naš model za prepoznavanje entiteta funkcionira na primjeru rečenice:\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": [
"## Ključne točke\n",
"\n",
"Čak i jednostavan LSTM model pokazuje zadovoljavajuće rezultate u prepoznavanju naziva entiteta (NER). Međutim, za postizanje znatno boljih rezultata, možda biste trebali koristiti velike unaprijed trenirane jezične modele poput BERT-a. Trening BERT-a za NER koristeći Huggingface Transformers biblioteku opisan je [ovdje](https://huggingface.co/course/chapter7/2?fw=pt).\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"\n---\n\n**Odricanje od odgovornosti**: \nOvaj dokument je preveden korištenjem AI usluge za prevođenje [Co-op Translator](https://github.com/Azure/co-op-translator). Iako nastojimo osigurati točnost, imajte na umu da automatski prijevodi mogu sadržavati pogreške ili netočnosti. Izvorni dokument na izvornom jeziku treba smatrati mjerodavnim izvorom. Za ključne informacije preporučuje se profesionalni prijevod od strane stručnjaka. Ne preuzimamo odgovornost za bilo kakva nesporazuma ili pogrešna tumačenja koja mogu proizaći iz korištenja ovog prijevoda.\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-30T08:07:45+00:00",
"source_file": "lessons/5-NLP/19-NER/NER-TF.ipynb",
"language_code": "hr"
}
},
"nbformat": 4,
"nbformat_minor": 2
}

View File

@ -0,0 +1,325 @@
{
"cells": [
{
"attachments": {},
"cell_type": "markdown",
"metadata": {},
"source": [
"## Eksperimentiranje s OpenAI GPT\n",
"\n",
"Ova bilježnica dio je [AI za početnike kurikuluma](http://aka.ms/ai-beginners).\n",
"\n",
"U ovoj bilježnici istražit ćemo kako se možemo igrati s OpenAI-GPT modelom koristeći Hugging Face biblioteku `transformers`.\n",
"\n",
"Bez daljnjeg odgađanja, pokrenimo pipeline za generiranje teksta i počnimo stvarati!\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": [
"## Inženjering upita\n",
"\n",
"U nekim problemima možete odmah koristiti openai-gpt generaciju tako da osmislite ispravne upite. Pogledajte primjere u nastavku:\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": [
"## Strategije uzorkovanja teksta\n",
"\n",
"Do sada smo koristili jednostavnu strategiju uzorkovanja **greedy** (pohlepno), gdje smo birali sljedeću riječ na temelju najveće vjerojatnosti. Evo kako to funkcionira:\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": [
"**Pretraživanje snopova** omogućuje generatoru istraživanje nekoliko smjerova (*snopova*) generiranja teksta i odabir onih s višim ukupnim rezultatom. Možete provesti pretraživanje snopova pružanjem parametra `num_beams`. Također možete odrediti `no_repeat_ngram_size` kako biste kaznili model za ponavljanje n-grama određene veličine:\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": [
"**Uzorkovanje** odabire sljedeću riječ nedeterministički, koristeći raspodjelu vjerojatnosti koju vraća model. Uzorkovanje uključujete pomoću parametra `do_sample=True`. Također možete odrediti `temperature` kako biste model učinili više ili manje determinističkim.\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": [
"Također možemo dodati dodatne parametre za uzorkovanje:\n",
"* `top_k` određuje broj opcija riječi koje treba uzeti u obzir prilikom korištenja uzorkovanja. Ovo smanjuje šansu za dobivanje čudnih (nisko-vjerojatnih) riječi u našem tekstu.\n",
"* `top_p` je slično, ali biramo najmanji podskup najvjerojatnijih riječi, čija ukupna vjerojatnost premašuje p.\n",
"\n",
"Slobodno eksperimentirajte s dodavanjem tih parametara.\n"
]
},
{
"attachments": {},
"cell_type": "markdown",
"metadata": {},
"source": [
"## Fino podešavanje vaših modela\n",
"\n",
"Možete također [fino podesiti svoj model](https://learn.microsoft.com/en-us/azure/cognitive-services/openai/how-to/fine-tuning?pivots=programming-language-studio?WT.mc_id=academic-77998-bethanycheum) na vlastitom skupu podataka. Ovo će vam omogućiti prilagodbu stila teksta, dok zadržavate glavni dio jezičnog modela.\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"\n---\n\n**Odricanje od odgovornosti**: \nOvaj dokument je preveden pomoću AI usluge za prevođenje [Co-op Translator](https://github.com/Azure/co-op-translator). Iako nastojimo osigurati točnost, imajte na umu da automatski prijevodi mogu sadržavati pogreške ili netočnosti. Izvorni dokument na izvornom jeziku treba smatrati autoritativnim izvorom. Za ključne informacije preporučuje se profesionalni prijevod od strane ljudskog prevoditelja. Ne preuzimamo odgovornost za bilo kakve nesporazume ili pogrešne interpretacije koje proizlaze iz korištenja ovog prijevoda.\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-30T08:06:59+00:00",
"source_file": "lessons/5-NLP/20-LangModels/GPT-PyTorch.ipynb",
"language_code": "hr"
}
},
"nbformat": 4,
"nbformat_minor": 2
}

View File

@ -0,0 +1,49 @@
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Zadatak: Diofantove jednadžbe\n",
"\n",
"> Ovaj zadatak dio je [AI za početnike kurikuluma](http://github.com/microsoft/ai-for-beginners) i inspiriran je [ovim člankom](https://habr.com/post/128704/).\n",
"\n",
"Vaš cilj je riješiti takozvanu **Diofantovu jednadžbu** - jednadžbu s cijelim brojevima kao rješenjima i cijelim koeficijentima. Na primjer, razmotrite sljedeću jednadžbu:\n",
"\n",
"$$a+2b+3c+4d=30$$\n",
"\n",
"Potrebno je pronaći cijele brojeve $a$,$b$,$c$,$d\\in\\mathbb{N}$ koji zadovoljavaju ovu jednadžbu.\n",
"\n",
"Savjeti:\n",
"1. Možete razmotriti rješenja u intervalu [0;30]\n",
"1. Kao gen, razmislite o korištenju popisa vrijednosti korijena\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": []
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"\n---\n\n**Odricanje od odgovornosti**: \nOvaj dokument je preveden korištenjem AI usluge za prijevod [Co-op Translator](https://github.com/Azure/co-op-translator). Iako nastojimo osigurati točnost, imajte na umu da automatski prijevodi mogu sadržavati pogreške ili netočnosti. Izvorni dokument na izvornom jeziku treba smatrati mjerodavnim izvorom. Za ključne informacije preporučuje se profesionalni prijevod od strane stručnjaka. Ne preuzimamo odgovornost za bilo kakve nesporazume ili pogrešne interpretacije proizašle iz korištenja ovog prijevoda.\n"
]
}
],
"metadata": {
"language_info": {
"name": "python"
},
"orig_nbformat": 4,
"coopTranslator": {
"original_hash": "a967e1fa1e11ab2b6467b19349a4a9aa",
"translation_date": "2025-08-30T07:07:58+00:00",
"source_file": "lessons/6-Other/21-GeneticAlgorithms/Diophantine.ipynb",
"language_code": "hr"
}
},
"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": [
"# Treniranje RL-a za balansiranje Cartpolea\n",
"\n",
"Ova bilježnica dio je [kurikuluma AI za početnike](http://aka.ms/ai-beginners). Inspirirana je [službenim PyTorch vodičem](https://pytorch.org/tutorials/intermediate/reinforcement_q_learning.html) i [ovom PyTorch implementacijom za Cartpole](https://github.com/yc930401/Actor-Critic-pytorch).\n",
"\n",
"U ovom primjeru koristit ćemo RL za treniranje modela koji balansira štap na kolicima koja se mogu kretati lijevo i desno na horizontalnoj skali. Koristit ćemo okruženje [OpenAI Gym](https://www.gymlibrary.ml/) za simulaciju štapa.\n",
"\n",
"> **Napomena**: Kod ove lekcije možete pokrenuti lokalno (npr. iz Visual Studio Code-a), u kojem će se slučaju simulacija otvoriti u novom prozoru. Ako kod pokrećete online, možda ćete trebati napraviti neke prilagodbe koda, kako je opisano [ovdje](https://towardsdatascience.com/rendering-openai-gym-envs-on-binder-and-google-colab-536f99391cc7).\n",
"\n",
"Započet ćemo tako da provjerimo je li Gym instaliran:\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"import sys\n",
"!{sys.executable} -m pip install gym"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Sada ćemo stvoriti CartPole okruženje i vidjeti kako njime upravljati. Okruženje ima sljedeće značajke:\n",
"\n",
"* **Prostor akcija** je skup mogućih akcija koje možemo izvršiti u svakom koraku simulacije\n",
"* **Prostor opažanja** je prostor opažanja koje možemo napraviti\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": [
"Pogledajmo kako simulacija funkcionira. Sljedeća petlja pokreće simulaciju dok `env.step` ne vrati zastavicu za završetak `done`. Akcije ćemo nasumično birati pomoću `env.action_space.sample()`, što znači da će eksperiment vjerojatno vrlo brzo propasti (okruženje CartPole završava kada brzina CartPole-a, njegova pozicija ili kut budu izvan određenih granica).\n",
"\n",
"> Simulacija će se otvoriti u novom prozoru. Kod možete pokrenuti nekoliko puta i vidjeti kako se ponaša.\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": [
"Možete primijetiti da opažanja sadrže 4 broja. Oni su:\n",
"- Položaj kolica\n",
"- Brzina kolica\n",
"- Kut stupa\n",
"- Brzina rotacije stupa\n",
"\n",
"`rew` je nagrada koju primamo pri svakom koraku. Možete vidjeti da u CartPole okruženju dobivate 1 bod za svaki korak simulacije, a cilj je maksimizirati ukupnu nagradu, tj. vrijeme tijekom kojeg CartPole može održavati ravnotežu bez pada.\n",
"\n",
"Tijekom učenja pojačanjem, naš cilj je trenirati **politiku** $\\pi$, koja će za svako stanje $s$ odrediti koju akciju $a$ trebamo poduzeti, dakle u suštini $a = \\pi(s)$.\n",
"\n",
"Ako želite probabilističko rješenje, možete razmišljati o politici kao o vraćanju skupa vjerojatnosti za svaku akciju, tj. $\\pi(a|s)$ bi značilo vjerojatnost da trebamo poduzeti akciju $a$ u stanju $s$.\n",
"\n",
"## Metoda gradijenta politike\n",
"\n",
"U najjednostavnijem algoritmu za učenje pojačanjem, nazvanom **Gradijent politike**, trenirat ćemo neuronsku mrežu da predvidi sljedeću akciju.\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": [
"Trenirat ćemo mrežu izvođenjem mnogih eksperimenata i ažuriranjem naše mreže nakon svakog izvođenja. Definirajmo funkciju koja će izvršiti eksperiment i vratiti rezultate (tzv. **trag**) - sva stanja, akcije (i njihove preporučene vjerojatnosti) i nagrade:\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": [
"Možete pokrenuti jednu epizodu s neuvježbanom mrežom i primijetiti da je ukupna nagrada (poznata i kao duljina epizode) vrlo niska:\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": [
"Jedan od izazovnih aspekata algoritma gradijenta politike je korištenje **diskontiranih nagrada**. Ideja je da izračunamo vektor ukupnih nagrada u svakom koraku igre, a tijekom tog procesa diskontiramo rane nagrade koristeći neki koeficijent $gamma$. Također normaliziramo dobiveni vektor, jer ćemo ga koristiti kao težinu za utjecaj na naše treniranje:\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": [
"Sada krenimo s pravim treningom! Izvest ćemo 300 epizoda, a u svakoj epizodi napravit ćemo sljedeće:\n",
"\n",
"1. Provesti eksperiment i prikupiti trag.\n",
"2. Izračunati razliku (`gradients`) između poduzetih akcija i predviđenih vjerojatnosti. Što je razlika manja, to smo sigurniji da smo poduzeli ispravnu akciju.\n",
"3. Izračunati diskontirane nagrade i pomnožiti gradijente s diskontiranim nagradama - to će osigurati da koraci s višim nagradama imaju veći utjecaj na konačni rezultat od onih s nižim nagradama.\n",
"4. Očekivane ciljne akcije za našu neuronsku mrežu djelomično će se uzimati iz predviđenih vjerojatnosti tijekom izvođenja, a djelomično iz izračunatih gradijenata. Koristit ćemo parametar `alpha` kako bismo odredili u kojoj mjeri se uzimaju u obzir gradijenti i nagrade - to se naziva *stopa učenja* algoritma za pojačanje.\n",
"5. Na kraju, treniramo našu mrežu na stanjima i očekivanim akcijama te ponavljamo proces.\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": [
"Sada pokrenimo epizodu s renderiranjem kako bismo vidjeli rezultat:\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"_ = run_episode(render=True)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Nadamo se da sada možete vidjeti da se štap prilično dobro može balansirati!\n",
"\n",
"## Model Akter-Kritičar\n",
"\n",
"Model Akter-Kritičar je daljnji razvoj politika gradijenata, u kojem gradimo neuronsku mrežu kako bismo naučili i politiku i procijenjene nagrade. Mreža će imati dva izlaza (ili to možete gledati kao dvije odvojene mreže):\n",
"* **Akter** će preporučiti akciju koju treba poduzeti dajući nam distribuciju vjerojatnosti stanja, kao u modelu politika gradijenata.\n",
"* **Kritičar** bi procijenio kakva bi nagrada bila od tih akcija. Vraća ukupne procijenjene nagrade u budućnosti za dano stanje.\n",
"\n",
"Definirajmo takav model:\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": [
"Morali bismo malo izmijeniti naše funkcije `discounted_rewards` i `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": [
"Sada ćemo pokrenuti glavnu petlju za treniranje. Koristit ćemo ručni proces treniranja mreže izračunavanjem odgovarajućih funkcija gubitka i ažuriranjem parametara mreže:\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": [
"## Ključne točke\n",
"\n",
"Vidjeli smo dva algoritma za pojačano učenje u ovom demo prikazu: jednostavni policy gradient i sofisticiraniji actor-critic. Možete primijetiti da ti algoritmi rade s apstraktnim pojmovima stanja, akcije i nagrade - zbog čega se mogu primijeniti na vrlo različite okruženja.\n",
"\n",
"Pojačano učenje omogućuje nam da naučimo najbolju strategiju za rješavanje problema samo promatranjem konačne nagrade. Činjenica da nam nisu potrebni označeni skupovi podataka omogućuje nam da više puta ponavljamo simulacije kako bismo optimizirali naše modele. Ipak, još uvijek postoje mnogi izazovi u pojačanom učenju, koje možete istražiti ako odlučite posvetiti više pažnje ovom zanimljivom području umjetne inteligencije.\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"\n---\n\n**Odricanje od odgovornosti**: \nOvaj dokument je preveden pomoću AI usluge za prevođenje [Co-op Translator](https://github.com/Azure/co-op-translator). Iako nastojimo osigurati točnost, imajte na umu da automatski prijevodi mogu sadržavati pogreške ili netočnosti. Izvorni dokument na izvornom jeziku treba smatrati autoritativnim izvorom. Za ključne informacije preporučuje se profesionalni prijevod od strane ljudskog prevoditelja. Ne preuzimamo odgovornost za bilo kakve nesporazume ili pogrešne interpretacije koje proizlaze iz korištenja ovog prijevoda.\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-30T07:11:55+00:00",
"source_file": "lessons/6-Other/22-DeepRL/CartPole-RL-PyTorch.ipynb",
"language_code": "hr"
}
},
"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": [
"# # Treniranje Mountain Car za bijeg\n",
"\n",
"Laboratorijska vježba iz [AI za početnike kurikuluma](https://github.com/microsoft/ai-for-beginners).\n",
"\n",
"Vaš cilj je trenirati RL agenta da kontrolira [Mountain Car](https://www.gymlibrary.ml/environments/classic_control/mountain_car/) u OpenAI okruženju.\n",
"\n",
"Krenimo s kreiranjem okruženja:\n"
]
},
{
"cell_type": "code",
"execution_count": 1,
"metadata": {},
"outputs": [],
"source": [
"import gym\n",
"env = gym.make('MountainCar-v0')"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Pogledajmo kako izgleda slučajni eksperiment:\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**Odricanje od odgovornosti**: \nOvaj dokument je preveden korištenjem AI usluge za prevođenje [Co-op Translator](https://github.com/Azure/co-op-translator). Iako nastojimo osigurati točnost, imajte na umu da automatski prijevodi mogu sadržavati pogreške ili netočnosti. Izvorni dokument na izvornom jeziku treba smatrati mjerodavnim izvorom. Za ključne informacije preporučuje se profesionalni prijevod od strane stručnjaka. Ne preuzimamo odgovornost za bilo kakva nesporazuma ili pogrešna tumačenja koja mogu proizaći iz korištenja ovog prijevoda.\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-30T07:14:15+00:00",
"source_file": "lessons/6-Other/22-DeepRL/lab/MountainCar.ipynb",
"language_code": "hr"
}
},
"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,8 +1,8 @@
<!--
CO_OP_TRANSLATOR_METADATA:
{
"original_hash": "f3a6b0ddf7e6e3f33b2a543baf086dc9",
"translation_date": "2025-08-25T21:16:56+00:00",
"original_hash": "07191303b7ea2aff1d47e2b0fe4bb862",
"translation_date": "2025-08-30T07:06:23+00:00",
"source_file": "README.md",
"language_code": "sl"
}
@ -23,21 +23,32 @@ CO_OP_TRANSLATOR_METADATA:
# Umetna inteligenca za začetnike - Kurikulum
|![ Sketchnote by [(@girlie_mac)](https://twitter.com/girlie_mac) ](./lessons/sketchnotes/ai-overview.png)|
|![Sketchnote by @girlie_mac https://twitter.com/girlie_mac](../../translated_images/ai-overview.0857791951d19500d0ef8b803d77110c738dcafc52306e6d68724742cd4af167.sl.png)|
|:---:|
| AI za začetnike - _Sketchnote by [@girlie_mac](https://twitter.com/girlie_mac)_ |
Raziskujte svet **umetne inteligence** (AI) z našim 12-tedenskim kurikulumom, ki vključuje 24 lekcij! Vsebuje praktične lekcije, kvize in laboratorijske vaje. Kurikulum je prijazen začetnikom in pokriva orodja, kot sta TensorFlow in PyTorch, ter etiko v AI.
## Kaj boste spoznali
### 🌐 Podpora za več jezikov
#### Podprto prek GitHub Action (samodejno in vedno posodobljeno)
[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](../ro/README.md) | [Bulgarian](../bg/README.md) | [Serbian (Cyrillic)](../sr/README.md) | [Croatian](../hr/README.md) | [Slovenian](./README.md) | [Ukrainian](../uk/README.md) | [Burmese (Myanmar)](../my/README.md)
**Če želite dodati dodatne jezike, so podprti jeziki navedeni [tukaj](https://github.com/Azure/co-op-translator/blob/main/getting_started/supported-languages.md)**
## Pridružite se skupnosti
[![Azure AI Discord](https://dcbadge.limes.pink/api/server/kzRShWzttr)](https://discord.gg/kzRShWzttr)
## Kaj se boste naučili
**[Miselni zemljevid tečaja](http://soshnikov.com/courses/ai-for-beginners/mindmap.html)**
V tem kurikulumu boste spoznali:
V tem kurikulumu se boste naučili:
* Različne pristope k umetni inteligenci, vključno z "dobrim starim" simbolnim pristopom z **predstavitvijo znanja** in sklepanjem ([GOFAI](https://en.wikipedia.org/wiki/Symbolic_artificial_intelligence)).
* **Nevronske mreže** in **globoko učenje**, ki sta jedro sodobne umetne inteligence. Koncepte teh pomembnih tem bomo ponazorili s kodo v dveh najbolj priljubljenih ogrodjih - [TensorFlow](http://Tensorflow.org) in [PyTorch](http://pytorch.org).
* **Nevronske arhitekture** za delo s slikami in besedilom. Pokrili bomo nedavne modele, vendar morda ne bomo zajeli najnovejših dosežkov.
* **Nevronske mreže** in **globoko učenje**, ki sta jedro sodobne AI. Koncepte za temi pomembnimi temami bomo ponazorili s kodo v dveh najbolj priljubljenih ogrodjih - [TensorFlow](http://Tensorflow.org) in [PyTorch](http://pytorch.org).
* **Nevronske arhitekture** za delo s slikami in besedilom. Pokrili bomo nedavne modele, vendar morda nekoliko manj najnovejše dosežke.
* Manj priljubljene pristope AI, kot so **genetski algoritmi** in **sistemi z več agenti**.
Kaj ne bomo pokrili v tem kurikulumu:
@ -46,12 +57,12 @@ Kaj ne bomo pokrili v tem kurikulumu:
* Poslovne primere uporabe **AI v poslovanju**. Razmislite o tečaju [Uvod v AI za poslovne uporabnike](https://docs.microsoft.com/learn/paths/introduction-ai-for-business-users/?WT.mc_id=academic-77998-bethanycheum) na Microsoft Learn ali [AI Business School](https://www.microsoft.com/ai/ai-business-school/?WT.mc_id=academic-77998-bethanycheum), razvit v sodelovanju z [INSEAD](https://www.insead.edu/).
* **Klasično strojno učenje**, ki je dobro opisano v našem [kurikulumu Strojno učenje za začetnike](http://github.com/Microsoft/ML-for-Beginners).
* Praktične aplikacije AI, zgrajene z uporabo **[Cognitive Services](https://azure.microsoft.com/services/cognitive-services/?WT.mc_id=academic-77998-bethanycheum)**. Za to priporočamo, da začnete z moduli Microsoft Learn za [vizijo](https://docs.microsoft.com/learn/paths/create-computer-vision-solutions-azure-cognitive-services/?WT.mc_id=academic-77998-bethanycheum), [obdelavo naravnega jezika](https://docs.microsoft.com/learn/paths/explore-natural-language-processing/?WT.mc_id=academic-77998-bethanycheum), **[Generativno AI z Azure OpenAI Service](https://learn.microsoft.com/en-us/training/paths/develop-ai-solutions-azure-openai/?WT.mc_id=academic-77998-bethanycheum)** in druge.
* Specifična **oblaka ML ogrodja**, kot so [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) ali [Azure Databricks](https://docs.microsoft.com/learn/paths/data-engineer-azure-databricks?WT.mc_id=academic-77998-bethanycheum). Razmislite o uporabi učnih poti [Build and operate machine learning solutions with Azure Machine Learning](https://docs.microsoft.com/learn/paths/build-ai-solutions-with-azure-ml-service/?WT.mc_id=academic-77998-bethanycheum) in [Build and Operate Machine Learning Solutions with Azure Databricks](https://docs.microsoft.com/learn/paths/build-operate-machine-learning-solutions-azure-databricks/?WT.mc_id=academic-77998-bethanycheum).
* **Pogovorna AI** in **klepetalni roboti**. Obstaja ločena učna pot [Ustvarite pogovorne AI rešitve](https://docs.microsoft.com/learn/paths/create-conversational-ai-solutions/?WT.mc_id=academic-77998-bethanycheum), prav tako pa lahko preberete [ta blog prispevek](https://soshnikov.com/azure/hello-bot-conversational-ai-on-microsoft-platform/) za več podrobnosti.
* Praktične aplikacije AI, zgrajene z uporabo **[Cognitive Services](https://azure.microsoft.com/services/cognitive-services/?WT.mc_id=academic-77998-bethanycheum)**. Za to priporočamo, da začnete z moduli Microsoft Learn za [vizijo](https://docs.microsoft.com/learn/paths/create-computer-vision-solutions-azure-cognitive-services/?WT.mc_id=academic-77998-bethanycheum), [obdelavo naravnega jezika](https://docs.microsoft.com/learn/paths/explore-natural-language-processing/?WT.mc_id=academic-77998-bethanycheum), **[Generativna AI z Azure OpenAI Service](https://learn.microsoft.com/en-us/training/paths/develop-ai-solutions-azure-openai/?WT.mc_id=academic-77998-bethanycheum)** in druge.
* Specifična ML **oblaka ogrodja**, kot so [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) ali [Azure Databricks](https://docs.microsoft.com/learn/paths/data-engineer-azure-databricks?WT.mc_id=academic-77998-bethanycheum). Razmislite o uporabi učnih poti [Build and operate machine learning solutions with Azure Machine Learning](https://docs.microsoft.com/learn/paths/build-ai-solutions-with-azure-ml-service/?WT.mc_id=academic-77998-bethanycheum) in [Build and Operate Machine Learning Solutions with Azure Databricks](https://docs.microsoft.com/learn/paths/build-operate-machine-learning-solutions-azure-databricks/?WT.mc_id=academic-77998-bethanycheum).
* **Pogovorna AI** in **klepetalni roboti**. Obstaja ločena učna pot [Create conversational AI solutions](https://docs.microsoft.com/learn/paths/create-conversational-ai-solutions/?WT.mc_id=academic-77998-bethanycheum), prav tako pa si lahko ogledate [ta blog prispevek](https://soshnikov.com/azure/hello-bot-conversational-ai-on-microsoft-platform/) za več podrobnosti.
* **Globoka matematika** za globoko učenje. Za to priporočamo knjigo [Deep Learning](https://www.amazon.com/Deep-Learning-Adaptive-Computation-Machine/dp/0262035618) avtorjev Ian Goodfellow, Yoshua Bengio in Aaron Courville, ki je na voljo tudi na spletu na [https://www.deeplearningbook.org/](https://www.deeplearningbook.org/).
Za uvod v teme _AI v oblaku_ razmislite o učni poti [Začnite z umetno inteligenco na Azure](https://docs.microsoft.com/learn/paths/get-started-with-artificial-intelligence-on-azure/?WT.mc_id=academic-77998-bethanycheum).
Za uvod v teme _AI v oblaku_ razmislite o učni poti [Get started with artificial intelligence on Azure](https://docs.microsoft.com/learn/paths/get-started-with-artificial-intelligence-on-azure/?WT.mc_id=academic-77998-bethanycheum).
# Vsebina
@ -61,48 +72,48 @@ Za uvod v teme _AI v oblaku_ razmislite o učni poti [Začnite z umetno intelige
| I | [**Uvod v AI**](./lessons/1-Intro/README.md) | | |
| 01 | [Uvod in zgodovina AI](./lessons/1-Intro/README.md) | - | - |
| II | **Simbolna AI** |
| 02 | [Predstavitev znanja in ekspertni sistemi](./lessons/2-Symbolic/README.md) | [Ekspertni sistemi](https://github.com/microsoft/AI-For-Beginners/blob/main/lessons/2-Symbolic/Animals.ipynb) / [Ontologija](https://github.com/microsoft/AI-For-Beginners/blob/main/lessons/2-Symbolic/FamilyOntology.ipynb) /[Konceptni graf](https://github.com/microsoft/AI-For-Beginners/blob/main/lessons/2-Symbolic/MSConceptGraph.ipynb) | |
| 02 | [Predstavitev znanja in ekspertni sistemi](./lessons/2-Symbolic/README.md) | [Ekspertni sistemi](./lessons/2-Symbolic/Animals.ipynb) / [Ontologija](./lessons/2-Symbolic/FamilyOntology.ipynb) /[Konceptni graf](./lessons/2-Symbolic/MSConceptGraph.ipynb) | |
| III | [**Uvod v nevronske mreže**](./lessons/3-NeuralNetworks/README.md) |||
| 03 | [Perceptron](./lessons/3-NeuralNetworks/03-Perceptron/README.md) | [Zvezek](https://github.com/microsoft/AI-For-Beginners/blob/main/lessons/3-NeuralNetworks/03-Perceptron/Perceptron.ipynb) | [Laboratorij](./lessons/3-NeuralNetworks/03-Perceptron/lab/README.md) |
| 04 | [Večplastni perceptron in ustvarjanje lastnega ogrodja](./lessons/3-NeuralNetworks/04-OwnFramework/README.md) | [Zvezek](https://github.com/microsoft/AI-For-Beginners/blob/main/lessons/3-NeuralNetworks/04-OwnFramework/OwnFramework.ipynb) | [Laboratorij](./lessons/3-NeuralNetworks/04-OwnFramework/lab/README.md) |
| 05 | [Uvod v ogrodja (PyTorch/TensorFlow) in prenaučenje](./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) | [Laboratorij](./lessons/3-NeuralNetworks/05-Frameworks/lab/README.md) |
| IV | [**Računalniški vid**](./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)| [Raziskovanje računalniškega vida na Microsoft Azure](https://learn.microsoft.com/en-us/collections/7w28iy2xrqzdj0?WT.mc_id=academic-77998-bethanycheum) |
| 06 | [Uvod v računalniški vid. OpenCV](./lessons/4-ComputerVision/06-IntroCV/README.md) | [Zvezek](https://github.com/microsoft/AI-For-Beginners/blob/main/lessons/4-ComputerVision/06-IntroCV/OpenCV.ipynb) | [Laboratorij](./lessons/4-ComputerVision/06-IntroCV/lab/README.md) |
| 07 | [Konvolucijske nevronske mreže](./lessons/4-ComputerVision/07-ConvNets/README.md) & [Arhitekture 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) | [Laboratorij](./lessons/4-ComputerVision/07-ConvNets/lab/README.md) |
| 08 | [Vnaprej naučena omrežja in prenos učenja](./lessons/4-ComputerVision/08-TransferLearning/README.md) in [Triki za učenje](./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) | [Laboratorij](./lessons/4-ComputerVision/08-TransferLearning/lab/README.md) |
| 09 | [Avtoenkoderji in 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 | [Generativna nasprotujoča si omrežja in prenos umetniškega sloga](./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 | [Prepoznavanje objektov](./lessons/4-ComputerVision/11-ObjectDetection/README.md) | [TensorFlow](https://github.com/microsoft/AI-For-Beginners/blob/main/lessons/4-ComputerVision/11-ObjectDetection/ObjectDetection.ipynb) | [Laboratorij](./lessons/4-ComputerVision/11-ObjectDetection/lab/README.md) |
| 12 | [Semantična segmentacija. 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) | [Zvezek](./lessons/3-NeuralNetworks/03-Perceptron/Perceptron.ipynb) | [Laboratorij](./lessons/3-NeuralNetworks/03-Perceptron/lab/README.md) |
| 04 | [Večplastni perceptron in ustvarjanje lastnega ogrodja](./lessons/3-NeuralNetworks/04-OwnFramework/README.md) | [Zvezek](./lessons/3-NeuralNetworks/04-OwnFramework/OwnFramework.ipynb) | [Laboratorij](./lessons/3-NeuralNetworks/04-OwnFramework/lab/README.md) |
| 05 | [Uvod v ogrodja (PyTorch/TensorFlow) in 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) | [Laboratorij](./lessons/3-NeuralNetworks/05-Frameworks/lab/README.md) |
| IV | [**Računalniški vid**](./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)| [Raziščite računalniški vid na Microsoft Azure](https://learn.microsoft.com/en-us/collections/7w28iy2xrqzdj0?WT.mc_id=academic-77998-bethanycheum) |
| 06 | [Uvod v računalniški vid. OpenCV](./lessons/4-ComputerVision/06-IntroCV/README.md) | [Zvezek](./lessons/4-ComputerVision/06-IntroCV/OpenCV.ipynb) | [Laboratorij](./lessons/4-ComputerVision/06-IntroCV/lab/README.md) |
| 07 | [Konvolucijske nevronske mreže](./lessons/4-ComputerVision/07-ConvNets/README.md) & [Arhitekture 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) | [Laboratorij](./lessons/4-ComputerVision/07-ConvNets/lab/README.md) |
| 08 | [Vnaprej naučene mreže in prenos učenja](./lessons/4-ComputerVision/08-TransferLearning/README.md) in [Triki za učenje](./lessons/4-ComputerVision/08-TransferLearning/TrainingTricks.md) | [PyTorch](./lessons/4-ComputerVision/08-TransferLearning/TransferLearningPyTorch.ipynb) / [TensorFlow](./lessons/3-NeuralNetworks/05-Frameworks/IntroKerasTF.ipynb) | [Laboratorij](./lessons/4-ComputerVision/08-TransferLearning/lab/README.md) |
| 09 | [Avtoenkoderji in 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 | [Generativne nasprotujoče mreže in prenos umetniškega sloga](./lessons/4-ComputerVision/10-GANs/README.md) | [PyTorch](./lessons/4-ComputerVision/10-GANs/GANPyTorch.ipynb) / [TensorFlow](./lessons/4-ComputerVision/10-GANs/GANTF.ipynb) | |
| 11 | [Prepoznavanje objektov](./lessons/4-ComputerVision/11-ObjectDetection/README.md) | [TensorFlow](./lessons/4-ComputerVision/11-ObjectDetection/ObjectDetection.ipynb) | [Laboratorij](./lessons/4-ComputerVision/11-ObjectDetection/lab/README.md) |
| 12 | [Semantična segmentacija. 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 | [**Obdelava naravnega jezika**](./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) | [Raziščite obdelavo naravnega jezika na Microsoft Azure](https://learn.microsoft.com/en-us/collections/7w28iy2xrqzdj0?WT.mc_id=academic-77998-bethanycheum)|
| 13 | [Predstavitev besedila. 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 | [Semantične besedne vektorske predstavitve. Word2Vec in 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 | [Jezikovno modeliranje. Ustvarjanje lastnih vektorskih predstavitev](./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) | [Laboratorij](./lessons/5-NLP/15-LanguageModeling/lab/README.md) |
| 16 | [Rekurentna nevronska omrežja](./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 | [Generativna rekurentna omrežja](./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) | [Laboratorij](./lessons/5-NLP/17-GenerativeNetworks/lab/README.md) |
| 18 | [Transformerji. 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 | [Prepoznavanje imenovanih entitet](./lessons/5-NLP/19-NER/README.md) | [TensorFlow](https://microsoft.github.io/AI-For-Beginners/lessons/5-NLP/19-NER/NER-TF.ipynb) | [Laboratorij](./lessons/5-NLP/19-NER/lab/README.md) |
| 20 | [Veliki jezikovni modeli, programiranje z navodili in naloge z malo primeri](./lessons/5-NLP/20-LangModels/READMELargeLang.md) | [PyTorch](https://microsoft.github.io/AI-For-Beginners/lessons/5-NLP/20-LangModels/GPT-PyTorch.ipynb) | |
| 13 | [Predstavitev besedila. 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 | [Semantične besedne vektorske predstavitve. Word2Vec in 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 | [Jezikovno modeliranje. Učenje lastnih predstavitev](./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) | [Laboratorij](./lessons/5-NLP/15-LanguageModeling/lab/README.md) |
| 16 | [Rekurentne nevronske mreže](./lessons/5-NLP/16-RNN/README.md) | [PyTorch](./lessons/5-NLP/16-RNN/RNNPyTorch.ipynb) / [TensorFlow](./lessons/5-NLP/16-RNN/RNNTF.ipynb) | |
| 17 | [Generativne rekurentne mreže](./lessons/5-NLP/17-GenerativeNetworks/README.md) | [PyTorch](./lessons/5-NLP/17-GenerativeNetworks/GenerativePyTorch.md) / [TensorFlow](./lessons/5-NLP/17-GenerativeNetworks/GenerativeTF.md) | [Laboratorij](./lessons/5-NLP/17-GenerativeNetworks/lab/README.md) |
| 18 | [Transformerji. 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 | [Prepoznavanje imenovanih entitet](./lessons/5-NLP/19-NER/README.md) | [TensorFlow](./lessons/5-NLP/19-NER/NER-TF.ipynb) | [Laboratorij](./lessons/5-NLP/19-NER/lab/README.md) |
| 20 | [Veliki jezikovni modeli, programiranje s pozivi in naloge z malo primeri](./lessons/5-NLP/20-LangModels/READMELargeLang.md) | [PyTorch](./lessons/5-NLP/20-LangModels/GPT-PyTorch.ipynb) | |
| VI | **Druge tehnike umetne inteligence** || |
| 21 | [Genetski algoritmi](./lessons/6-Other/21-GeneticAlgorithms/README.md) | [Zvezek](https://github.com/microsoft/AI-For-Beginners/blob/main/lessons/6-Other/21-GeneticAlgorithms/Genetic.ipynb) | |
| 22 | [Globoko okrepljeno učenje](./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) | [Laboratorij](./lessons/6-Other/22-DeepRL/lab/README.md) |
| 21 | [Genetski algoritmi](./lessons/6-Other/21-GeneticAlgorithms/README.md) | [Zvezek](./lessons/6-Other/21-GeneticAlgorithms/Genetic.ipynb) | |
| 22 | [Globoko okrepljeno učenje](./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) | [Laboratorij](./lessons/6-Other/22-DeepRL/lab/README.md) |
| 23 | [Sistemi z več agenti](./lessons/6-Other/23-MultiagentSystems/README.md) | | |
| VII | **Etika umetne inteligence** | | |
| 24 | [Etika umetne inteligence in odgovorna umetna inteligenca](./lessons/7-Ethics/README.md) | [Microsoft Learn: Načela odgovorne umetne inteligence](https://docs.microsoft.com/learn/paths/responsible-ai-business-principles/?WT.mc_id=academic-77998-cacaste) | |
| IX | **Dodatki** | | |
| 25 | [Večmodalna omrežja, CLIP in VQGAN](./lessons/X-Extras/X1-MultiModal/README.md) | [Zvezek](https://github.com/microsoft/AI-For-Beginners/blob/main/lessons/X-Extras/X1-MultiModal/Clip.ipynb) | |
| 25 | [Večmodalne mreže, CLIP in VQGAN](./lessons/X-Extras/X1-MultiModal/README.md) | [Zvezek](./lessons/X-Extras/X1-MultiModal/Clip.ipynb) | |
## Vsaka lekcija vsebuje
* Gradivo za predhodno branje
* Izvedljive Jupyterjeve zvezke, ki so pogosto specifični za ogrodje (**PyTorch** ali **TensorFlow**). Izvedljivi zvezek vsebuje tudi veliko teoretičnega gradiva, zato je za razumevanje teme potrebno preučiti vsaj eno različico zvezka (bodisi PyTorch bodisi TensorFlow).
* Izvedljive Jupyter zvezke, ki so pogosto specifični za ogrodje (**PyTorch** ali **TensorFlow**). Izvedljivi zvezek vsebuje tudi veliko teoretičnega gradiva, zato je za razumevanje teme potrebno prebrati vsaj eno različico zvezka (PyTorch ali TensorFlow).
* **Laboratorije**, ki so na voljo za nekatere teme in vam omogočajo, da preizkusite uporabo naučenega gradiva na določenem problemu.
* Nekateri razdelki vsebujejo povezave do modulov [**MS Learn**](https://learn.microsoft.com/en-us/collections/7w28iy2xrqzdj0?WT.mc_id=academic-77998-bethanycheum), ki pokrivajo sorodne teme.
## Začetek
- Pripravili smo [lekcijo za nastavitev](https://github.com/microsoft/AI-For-Beginners/blob/main/lessons/0-course-setup/setup.md), ki vam bo pomagala pri nastavitvi vašega razvojnega okolja. - Za izobraževalce smo pripravili tudi [lekcijo za nastavitev kurikuluma](https://github.com/microsoft/AI-For-Beginners/blob/main/lessons/0-course-setup/for-teachers.md)!
- Kako [zagnati kodo v VSCode ali Codepace](https://github.com/microsoft/AI-For-Beginners/blob/main/lessons/0-course-setup/how-to-run.md)
- Pripravili smo [lekcijo za nastavitev](./lessons/0-course-setup/setup.md), ki vam pomaga pri nastavitvi razvojnega okolja. - Za učitelje smo pripravili tudi [lekcijo za nastavitev kurikuluma](./lessons/0-course-setup/for-teachers.md)!
- Kako [zagnati kodo v VSCode ali Codepace](./lessons/0-course-setup/how-to-run.md)
Sledite tem korakom:
@ -114,12 +125,12 @@ Ne pozabite označiti (🌟) tega repozitorija, da ga boste lažje našli kasnej
## Spoznajte druge učence
Pridružite se našemu [uradnemu AI Discord strežniku](https://aka.ms/genai-discord?WT.mc_id=academic-105485-bethanycheum), da spoznate in se povežete z drugimi učenci, ki obiskujejo ta tečaj, ter prejmete podporo.
Pridružite se našemu [uradnemu AI Discord strežniku](https://aka.ms/genai-discord?WT.mc_id=academic-105485-bethanycheum), da spoznate in se povežete z drugimi učenci, ki obiskujejo ta tečaj, ter pridobite podporo.
Če imate povratne informacije o izdelku ali vprašanja med gradnjo, obiščite naš [Azure AI Foundry Developer Forum](https://aka.ms/foundry/forum)
## Kvizi
> **Opomba o kvizih**: Vsi kvizi se nahajajo v mapi Quiz-app v etc\quiz-app. Povezani so znotraj lekcij, aplikacijo za kvize pa je mogoče zagnati lokalno ali namestiti na Azure; sledite navodilom v mapi `quiz-app`. Postopoma se lokalizirajo.
> **Opomba o kvizih**: Vsi kvizi se nahajajo v mapi Quiz-app v etc\quiz-app, ali [na spletu tukaj](https://ff-quizzes.netlify.app/). Povezani so znotraj lekcij, aplikacijo za kvize pa je mogoče zagnati lokalno ali namestiti na Azure; sledite navodilom v mapi `quiz-app`. Postopoma se lokalizirajo.
## Potrebna pomoč
Imate predloge ali ste našli napake v črkovanju ali kodi? Odprite težavo ali ustvarite zahtevo za združitev.
@ -144,12 +155,14 @@ Naša ekipa ustvarja tudi druge učne načrte! Oglejte si:
- [Podatkovna znanost za začetnike](https://aka.ms/datascience-beginners)
- [Strojno učenje za začetnike](https://aka.ms/ml-beginners)
- [Kibernetska varnost za začetnike](https://github.com/microsoft/Security-101)
- [Spletni razvoj za začetnike](https://aka.ms/webdev-beginners)
- [Razvoj spletnih aplikacij za začetnike](https://aka.ms/webdev-beginners)
- [IoT za začetnike](https://aka.ms/iot-beginners)
- [Razvoj XR za začetnike](https://github.com/microsoft/xr-development-for-beginners)
- [Obvladovanje GitHub Copilot za agentno uporabo](https://github.com/microsoft/Mastering-GitHub-Copilot-for-Paired-Programming)
- [Obvladovanje GitHub Copilot za razvijalce C#/.NET](https://github.com/microsoft/mastering-github-copilot-for-dotnet-csharp-developers)
- [Izberite svojo lastno Copilot pustolovščino](https://github.com/microsoft/CopilotAdventures)
- [Izberite svojo Copilot pustolovščino](https://github.com/microsoft/CopilotAdventures)
---
**Omejitev odgovornosti**:
Ta dokument je bil preveden z uporabo storitve AI za prevajanje [Co-op Translator](https://github.com/Azure/co-op-translator). Čeprav si prizadevamo za natančnost, vas prosimo, da upoštevate, da lahko avtomatizirani prevodi vsebujejo napake ali netočnosti. Izvirni dokument v njegovem maternem jeziku je treba obravnavati kot avtoritativni vir. Za ključne informacije priporočamo profesionalni človeški prevod. Ne odgovarjamo za morebitna nesporazumevanja ali napačne razlage, ki izhajajo iz uporabe tega prevoda.
Ta dokument je bil preveden z uporabo storitve za strojno prevajanje [Co-op Translator](https://github.com/Azure/co-op-translator). Čeprav si prizadevamo za natančnost, vas prosimo, da upoštevate, da lahko avtomatizirani prevodi vsebujejo napake ali netočnosti. Izvirni dokument v njegovem izvirnem jeziku je treba obravnavati kot avtoritativni vir. Za ključne informacije priporočamo strokovno človeško prevajanje. Ne prevzemamo odgovornosti za morebitna nesporazuma ali napačne razlage, ki bi nastale zaradi uporabe tega prevoda.

View File

@ -0,0 +1,480 @@
{
"cells": [
{
"cell_type": "markdown",
"metadata": {
"collapsed": true
},
"source": [
"# Implementacija strokovnega sistema za živali\n",
"\n",
"Primer iz [Učnega načrta za začetnike v umetni inteligenci](http://github.com/microsoft/ai-for-beginners).\n",
"\n",
"V tem primeru bomo implementirali preprost sistem, ki temelji na znanju, za določanje živali na podlagi nekaterih fizičnih značilnosti. Sistem lahko predstavimo z naslednjim AND-OR drevesom (to je del celotnega drevesa, zlahka lahko dodamo še več pravil):\n",
"\n",
"![](../../../../translated_images/AND-OR-Tree.5592d2c70187f283703c8e9c0d69d6a786eb370f4ace67f9a7aae5ada3d260b0.sl.png)\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Naš lasten lupinski sistem strokovnega znanja z vzvratnim sklepanjem\n",
"\n",
"Poskusimo definirati preprost jezik za predstavitev znanja, ki temelji na produkcijskih pravilih. Za definiranje pravil bomo uporabili Python razrede kot ključne besede. Obstajale bodo tri osnovne vrste razredov:\n",
"* `Ask` predstavlja vprašanje, ki ga je treba zastaviti uporabniku. Vsebuje nabor možnih odgovorov.\n",
"* `If` predstavlja pravilo in je zgolj sintaktični sladkor za shranjevanje vsebine pravila.\n",
"* `AND`/`OR` sta razreda za predstavitev AND/OR vej drevesa. Preprosto shranjujeta seznam argumentov znotraj. Za poenostavitev kode je vsa funkcionalnost definirana v nadrejenem razredu `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": [
"V našem sistemu bi delovni spomin vseboval seznam **dejstev** kot **pari atribut-vrednost**. Znanstvena baza je lahko opredeljena kot en velik slovar, ki preslika dejanja (nova dejstva, ki bi jih bilo treba vnesti v delovni spomin) v pogoje, izražene kot AND-OR izrazi. Prav tako je mogoče nekatera dejstva `vprašati`.\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": [
"Za izvedbo povratnega sklepanja bomo definirali razred `Knowledgebase`. Vsebuje:\n",
"\n",
"* Delovni `spomin` - slovar, ki preslika atribute v vrednosti\n",
"* `Pravila` baze znanja v zgoraj določenem formatu\n",
"\n",
"Dve glavni metodi sta:\n",
"\n",
"* `get` za pridobitev vrednosti atributa, pri čemer po potrebi izvede sklepanje. Na primer, `get('color')` pridobi vrednost atributa barve (po potrebi vpraša in shrani vrednost za kasnejšo uporabo v delovnem spominu). Če vprašamo `get('color:blue')`, bo vprašal za barvo in nato vrnil vrednost `y`/`n`, odvisno od barve.\n",
"* `eval` izvaja dejansko sklepanje, tj. prehaja skozi AND/OR drevo, ocenjuje podcilje itd.\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": [
"Zdaj določimo našo bazo znanja o živalih in izvedimo posvetovanje. Upoštevajte, da vas bo ta klic spraševal vprašanja. Odgovorite lahko z vnosom `y`/`n` za vprašanja z da-ne odgovori ali z navedbo številke (0..N) za vprašanja z daljšimi odgovori z več možnostmi.\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": [
"## Uporaba PyKnow za naprej sklepanje\n",
"\n",
"V naslednjem primeru bomo poskusili implementirati naprej sklepanje z uporabo ene izmed knjižnic za predstavitev znanja, [PyKnow](https://github.com/buguroo/pyknow/). **PyKnow** je knjižnica za ustvarjanje sistemov za naprej sklepanje v Pythonu, zasnovana tako, da je podobna klasičnemu staremu sistemu [CLIPS](http://www.clipsrules.net/index.html).\n",
"\n",
"Naprej sklepanje bi lahko implementirali tudi sami brez večjih težav, vendar so naivne implementacije običajno manj učinkovite. Za bolj učinkovito ujemanje pravil se uporablja poseben algoritem [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": [
"Naš sistem bomo definirali kot razred, ki podrazreduje `KnowledgeEngine`. Vsako pravilo je definirano z ločeno funkcijo z anotacijo `@Rule`, ki določa, kdaj naj se pravilo sproži. Znotraj pravila lahko dodamo nova dejstva z uporabo funkcije `declare`, dodajanje teh dejstev pa bo povzročilo, da bo sklepni stroj z naprej usmerjenim sklepanjem poklical še nekaj drugih pravil.\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": [
"Ko enkrat definiramo bazo znanja, napolnimo delovni spomin z nekaterimi začetnimi dejstvi in nato pokličemo metodo `run()`, da izvedemo sklepanje. Kot rezultat lahko vidite, da so v delovni spomin dodana nova izpeljana dejstva, vključno s končnim dejstvom o živali (če smo pravilno nastavili vsa začetna dejstva).\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**Omejitev odgovornosti**: \nTa dokument je bil preveden z uporabo storitve za strojno prevajanje [Co-op Translator](https://github.com/Azure/co-op-translator). Čeprav si prizadevamo za natančnost, vas opozarjamo, da lahko avtomatizirani prevodi vsebujejo napake ali netočnosti. Izvirni dokument v njegovem izvirnem jeziku je treba obravnavati kot avtoritativni vir. Za ključne informacije priporočamo strokovni prevod s strani človeka. Ne prevzemamo odgovornosti za morebitna nesporazumevanja ali napačne razlage, ki bi izhajale iz uporabe tega prevoda.\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-30T07:42:18+00:00",
"source_file": "lessons/2-Symbolic/Animals.ipynb",
"language_code": "sl"
}
},
"nbformat": 4,
"nbformat_minor": 2
}

View File

@ -0,0 +1,595 @@
{
"cells": [
{
"cell_type": "markdown",
"metadata": {
"collapsed": true
},
"source": [
"# Ontologija družinskih odnosov\n",
"\n",
"Ta primer je del [učnega načrta AI za začetnike](http://github.com/microsoft/ai-for-beginners) in je bil navdihnjen s [tem blog prispevkom](https://habr.com/post/270857/).\n",
"\n",
"Vedno se mi zdi težko zapomniti si različne odnose med ljudmi v družini. V tem primeru bomo uporabili ontologijo, ki definira družinske odnose, in dejansko genealogijo, ter pokazali, kako lahko nato izvedemo samodejno sklepanje za iskanje vseh sorodnikov.\n",
"\n",
"### Pridobivanje genealogskega drevesa\n",
"\n",
"Kot primer bomo vzeli genealogijo [družine Romanovih](https://en.wikipedia.org/wiki/House_of_Romanov). Najpogostejši format za opisovanje družinskih odnosov je [GEDCOM](https://en.wikipedia.org/wiki/GEDCOM). Genealoško drevo družine Romanov bomo vzeli v GEDCOM formatu:\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": [
"Za uporabo GEDCOM datoteke lahko uporabimo knjižnico `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": [
"Ta knjižnica odpravi nekatere tehnične težave pri razčlenjevanju datotek, vendar nam še vedno omogoča dokaj nizkoten dostop do vseh posameznikov in družin v drevesu. Tukaj je, kako lahko razčlenimo datoteko in prikažemo seznam vseh posameznikov:\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": [
"Tukaj je, kako lahko pridobimo informacije o družinah. Upoštevajte, da nam to daje seznam **identifikatorjev**, ki jih moramo pretvoriti v imena, če želimo več jasnosti:\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": [
"### Pridobivanje družinske ontologije\n",
"\n",
"Poglejmo si [družinsko ontologijo](https://raw.githubusercontent.com/blokhin/genealogical-trees/master/data/header.ttl), ki je definirana kot niz trojčkov Semantičnega spleta. Ta ontologija določa odnose, kot so `isUncleOf`, `isCousinOf` in mnoge druge. Vsi ti odnosi so definirani z osnovnimi predikati `isMotherOf`, `isFatherOf`, `isBrotherOf` in `isSisterOf`. Uporabili bomo avtomatsko sklepanje, da iz ontologije izpeljemo vse ostale odnose.\n",
"\n",
"Tukaj je primer definicije lastnosti `isAuntOf`, ki je definirana kot kompozicija `isSisterOf` in `isParentOf` (*Teta je sestra enega od staršev*).\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": [
"### Konstruiranje ontologije za sklepanje\n",
"\n",
"Za enostavnost bomo ustvarili eno datoteko ontologije, ki bo vključevala izvirna pravila iz družinske ontologije ter dejstva o posameznikih iz naše GEDCOM datoteke. Pregledali bomo GEDCOM datoteko, iz nje pridobili informacije o družinah in posameznikih ter jih pretvorili v trojčke.\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": [
"### Izvajanje sklepanja\n",
"\n",
"Zdaj želimo uporabiti to ontologijo za sklepanje in poizvedovanje. Uporabili bomo [RDFLib](https://github.com/RDFLib), knjižnico za branje RDF grafov v različnih formatih, izvajanje poizvedb itd.\n",
"\n",
"Za logično sklepanje bomo uporabili knjižnico [OWL-RL](https://github.com/RDFLib/OWL-RL), ki nam omogoča ustvarjanje **Zaprtja** RDF grafa, tj. dodajanje vseh možnih konceptov in odnosov, ki jih je mogoče izpeljati.\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": [
"Odpremo datoteko ontologije in preverimo, koliko trojk vsebuje:\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": [
"Zdaj zgradimo zaprtje in poglejmo, kako se število trojk povečuje:\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": [
"### Poizvedovanje za sorodnike\n",
"\n",
"Zdaj lahko poizvedujemo po grafu, da vidimo različne odnose med ljudmi. Uporabimo lahko jezik **SPARQL** skupaj z metodo `query`. V našem primeru si oglejmo vse **strice** v našem družinskem drevesu:\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": [
"Poigrajte se z različnimi družinskimi odnosi. Na primer, lahko si ogledate relacijo `isAncestorOf`, ki rekurzivno določa vse prednike določene osebe.\n",
"\n",
"Na koncu pa pospravimo!\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**Omejitev odgovornosti**: \nTa dokument je bil preveden z uporabo storitve za strojno prevajanje [Co-op Translator](https://github.com/Azure/co-op-translator). Čeprav si prizadevamo za natančnost, vas prosimo, da se zavedate, da lahko avtomatizirani prevodi vsebujejo napake ali netočnosti. Izvirni dokument v njegovem izvirnem jeziku je treba obravnavati kot avtoritativni vir. Za ključne informacije priporočamo strokovno človeško prevajanje. Ne prevzemamo odgovornosti za morebitna nesporazumevanja ali napačne razlage, ki izhajajo iz uporabe tega prevoda.\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-30T07:40:59+00:00",
"source_file": "lessons/2-Symbolic/FamilyOntology.ipynb",
"language_code": "sl"
}
},
"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/) je obsežna taksonomija pojmov, pridobljenih z interneta, z relacijami `je-a` med koncepti.\n",
"\n",
"Context Graph je na voljo v dveh oblikah:\n",
" * Velika besedilna datoteka za prenos\n",
" * REST API\n",
"\n",
"Statistika:\n",
" * 5401933 edinstvenih konceptov,\n",
" * 12551613 edinstvenih primerov\n",
" * 87603947 relacij `je-a`\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Uporaba spletne storitve\n",
"\n",
"Spletna storitev ponuja različne klice za oceno verjetnosti, da koncept pripada različnim skupinam. Več informacij je na voljo [tukaj](https://concept.research.microsoft.com/Home/Api). \n",
"Tukaj je primer URL-ja za klic: `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": [
"Poskusimo kategorizirati naslove novic z uporabo nadrejenih konceptov. Za pridobitev naslovov novic bomo uporabili storitev [NewsApi.org](http://newsapi.org). Za uporabo storitve morate pridobiti svoj API ključ - obiščite spletno stran in se registrirajte za brezplačni razvijalski načrt.\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": [
"Najprej želimo izluščiti samostalnike iz naslovov novic. Za to bomo uporabili knjižnico `TextBlob`, ki močno poenostavi veliko tipičnih nalog obdelave naravnega jezika, kot je ta.\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": [
"Vidimo, da samostalniki ne tvorijo velikih tematskih skupin. Nadomestimo samostalnike z bolj splošnimi izrazi, pridobljenimi iz konceptnega grafa. To bo trajalo nekaj časa, ker izvajamo REST klic za vsako samostalniško besedno zvezo.\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**Omejitev odgovornosti**: \nTa dokument je bil preveden z uporabo storitve za strojno prevajanje [Co-op Translator](https://github.com/Azure/co-op-translator). Čeprav si prizadevamo za natančnost, vas prosimo, da se zavedate, da lahko avtomatizirani prevodi vsebujejo napake ali netočnosti. Izvirni dokument v njegovem izvirnem jeziku je treba obravnavati kot avtoritativni vir. Za ključne informacije priporočamo strokovno človeško prevajanje. Ne prevzemamo odgovornosti za morebitna nesporazumevanja ali napačne razlage, ki izhajajo iz uporabe tega prevoda.\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-30T07:41:27+00:00",
"source_file": "lessons/2-Symbolic/MSConceptGraph.ipynb",
"language_code": "sl"
}
},
"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:01:19+00:00",
"original_hash": "ba5d1eb353d20d3e7181066b3c424b99",
"translation_date": "2025-08-30T07:07:41+00:00",
"source_file": "lessons/3-NeuralNetworks/03-Perceptron/lab/README.md",
"language_code": "sl"
}
@ -19,13 +19,15 @@ Z uporabo kode, ki smo jo razvili v tej lekciji za binarno klasifikacijo ročno
1. Za vsako številko ustvarite nabor podatkov za binarno klasifikacijo "ta številka proti vsem ostalim številkam".
1. Natrenirajte 10 različnih perceptronov za binarno klasifikacijo (enega za vsako številko).
1. Definirajte funkcijo, ki bo razvrstila vhodno številko.
1. Definirajte funkcijo, ki bo klasificirala vhodno številko.
> **Namig**: Če združimo uteži vseh 10 perceptronov v eno matriko, lahko vseh 10 perceptronov uporabimo na vhodnih številkah z eno matriko množenja. Najverjetnejšo številko lahko nato najdemo z uporabo operacije `argmax` na izhodu.
> **Namig**: Če združimo uteži vseh 10 perceptronov v eno matriko, bi morali biti sposobni uporabiti vseh 10 perceptronov na vhodne številke z eno matrično množitvijo. Najverjetnejšo številko lahko nato najdemo z uporabo operacije `argmax` na izhodu.
## Začetni zvezek
Začnite nalogo z odpiranjem [PerceptronMultiClass.ipynb](../../../../../../lessons/3-NeuralNetworks/03-Perceptron/lab/PerceptronMultiClass.ipynb)
Začnite laboratorijsko nalogo z odpiranjem [PerceptronMultiClass.ipynb](PerceptronMultiClass.ipynb)
---
**Omejitev odgovornosti**:
Ta dokument je bil preveden z uporabo storitve AI za prevajanje [Co-op Translator](https://github.com/Azure/co-op-translator). Čeprav si prizadevamo za natančnost, vas prosimo, da upoštevate, da lahko avtomatizirani prevodi vsebujejo napake ali netočnosti. Izvirni dokument v njegovem maternem jeziku je treba obravnavati kot avtoritativni vir. Za ključne informacije priporočamo profesionalni človeški prevod. Ne prevzemamo odgovornosti za morebitna nesporazumevanja ali napačne razlage, ki bi nastale zaradi uporabe tega prevoda.
Ta dokument je bil preveden z uporabo storitve za strojno prevajanje [Co-op Translator](https://github.com/Azure/co-op-translator). Čeprav si prizadevamo za natančnost, vas prosimo, da upoštevate, da lahko avtomatizirani prevodi vsebujejo napake ali netočnosti. Izvirni dokument v njegovem izvirnem jeziku je treba obravnavati kot avtoritativni vir. Za ključne informacije priporočamo strokovno človeško prevajanje. Ne prevzemamo odgovornosti za morebitna nesporazumevanja ali napačne razlage, ki izhajajo iz uporabe tega prevoda.

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,183 @@
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Razvrščanje številk MNIST z našim ogrodjem\n",
"\n",
"Laboratorijska naloga iz [Učnega načrta za začetnike v umetni inteligenci](https://github.com/microsoft/ai-for-beginners).\n",
"\n",
"### Branje nabora podatkov\n",
"\n",
"Ta koda prenese nabor podatkov iz repozitorija na internetu. Nabor podatkov lahko tudi ročno kopirate iz imenika `/data` v repozitoriju učnega načrta za umetno inteligenco.\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": [
"Poglejmo, kakšna je oblika podatkov, ki jih imamo:\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": [
"### Razdelitev podatkov\n",
"\n",
"Za razdelitev podatkov na učni in testni nabor bomo uporabili 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": [
"### Navodila\n",
"\n",
"1. Vzemite osnovno kodo iz lekcije in jo prilepite v ta zvezek ali (še bolje) v ločen Python modul.\n",
"1. Definirajte in naučite perceptron z eno plastjo, pri tem pa opazujte natančnost pri učenju in validaciji med procesom učenja.\n",
"1. Poskusite ugotoviti, ali je prišlo do prekomernega prileganja (overfitting), in prilagodite parametre plasti za izboljšanje natančnosti.\n",
"1. Ponovite prejšnje korake za perceptrone z 2 in 3 plastmi. Poskusite eksperimentirati z različnimi aktivacijskimi funkcijami med plastmi.\n",
"1. Poskusite odgovoriti na naslednja vprašanja:\n",
" - Ali aktivacijska funkcija med plastmi vpliva na zmogljivost mreže?\n",
" - Ali za to nalogo potrebujemo mrežo z 2 ali 3 plastmi?\n",
" - Ste naleteli na kakšne težave pri učenju mreže? Še posebej, ko se je število plasti povečalo.\n",
" - Kako se uteži mreže obnašajo med učenjem? Lahko narišete graf največje absolutne vrednosti uteži glede na epoho, da razumete povezavo.\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": []
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"\n---\n\n**Omejitev odgovornosti**: \nTa dokument je bil preveden z uporabo storitve za strojno prevajanje [Co-op Translator](https://github.com/Azure/co-op-translator). Čeprav si prizadevamo za natančnost, vas prosimo, da upoštevate, da lahko avtomatizirani prevodi vsebujejo napake ali netočnosti. Izvirni dokument v njegovem izvirnem jeziku je treba obravnavati kot avtoritativni vir. Za ključne informacije priporočamo strokovno človeško prevajanje. Ne prevzemamo odgovornosti za morebitna nesporazumevanja ali napačne razlage, ki izhajajo iz uporabe tega prevoda.\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-30T07:45:47+00:00",
"source_file": "lessons/3-NeuralNetworks/04-OwnFramework/lab/MyFW_MNIST.ipynb",
"language_code": "sl"
}
},
"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": [
"## Zaznavanje gibanja dlani z uporabo optičnega toka\n",
"\n",
"Ta laboratorijska vaja je del [Učnega načrta za začetnike v umetni inteligenci](http://aka.ms/ai-beginners).\n",
"\n",
"Oglejte si [ta video](../../../../../../lessons/4-ComputerVision/06-IntroCV/lab/palm-movement.mp4), v katerem se dlan osebe premika levo/desno/gor/dol na stabilnem ozadju.\n",
"\n",
"**Vaš cilj** bo z uporabo optičnega toka določiti, kateri deli videa vsebujejo gibanja gor/dol/levo/desno.\n",
"\n",
"Začnite z zajemanjem sličic iz videa, kot je opisano v predavanju:\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# Code here"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Zdaj izračunajte goste optične tokovne okvirje, kot je opisano v predavanju, in pretvorite gost optični tok v polarne koordinate:\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# Code here"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Zgradite histogram smeri za vsak optični tok okvirja. Histogram prikazuje, koliko vektorjev spada v določen razpon, in naj loči različne smeri gibanja na okvirju.\n",
"\n",
"> Morda boste želeli izničiti vse vektorje, katerih velikost je pod določenim pragom. To bo odstranilo majhne dodatne premike v videu, kot so oči in glava.\n",
"\n",
"Prikažite histograme za nekatere okvirje.\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# Code here"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Pri pregledu histogramov bi moralo biti precej enostavno določiti smer gibanja. Izbrati morate tiste razrede, ki ustrezajo smerem gor/dol/levo/desno in so nad določenim pragom.\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# Code here"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Čestitamo! Če ste opravili vse zgornje korake, ste zaključili laboratorijsko vajo!\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"\n---\n\n**Omejitev odgovornosti**: \nTa dokument je bil preveden z uporabo storitve za strojno prevajanje [Co-op Translator](https://github.com/Azure/co-op-translator). Čeprav si prizadevamo za natančnost, vas prosimo, da upoštevate, da lahko avtomatizirani prevodi vsebujejo napake ali netočnosti. Izvirni dokument v njegovem izvirnem jeziku je treba obravnavati kot avtoritativni vir. Za ključne informacije priporočamo strokovno človeško prevajanje. Ne prevzemamo odgovornosti za morebitna nesporazumevanja ali napačne razlage, ki izhajajo iz uporabe tega prevoda.\n"
]
}
],
"metadata": {
"language_info": {
"name": "python"
},
"orig_nbformat": 4,
"coopTranslator": {
"original_hash": "153d9e417e079bf62f8f693002d0deaf",
"translation_date": "2025-08-30T07:29:30+00:00",
"source_file": "lessons/4-ComputerVision/06-IntroCV/lab/MovementDetection.ipynb",
"language_code": "sl"
}
},
"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": [
"# Naloga razvrščanja besedila\n",
"\n",
"Kot smo že omenili, se bomo osredotočili na preprosto nalogo razvrščanja besedila na podlagi podatkovnega nabora **AG_NEWS**, kjer razvrščamo naslove novic v eno od 4 kategorij: Svet, Šport, Posel in Znanost/Tehnologija.\n",
"\n",
"## Podatkovni nabor\n",
"\n",
"Ta podatkovni nabor je vgrajen v modul [`torchtext`](https://github.com/pytorch/text), zato imamo do njega enostaven dostop.\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": [
"Tukaj `train_dataset` in `test_dataset` vsebujeta zbirke, ki vračajo pare oznake (številka razreda) in besedila, na primer:\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": [
"Torej, natisnimo prvih 10 novih naslovov iz našega nabora podatkov:\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": [
"Ker so podatkovne zbirke iteratorji, jih moramo, če želimo podatke uporabiti večkrat, pretvoriti v seznam:\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": [
"## Tokenizacija\n",
"\n",
"Zdaj moramo besedilo pretvoriti v **številke**, ki jih lahko predstavimo kot tenzorje. Če želimo predstavitev na ravni besed, moramo narediti dve stvari:\n",
"* uporabiti **tokenizator**, da razdelimo besedilo na **tokene**\n",
"* zgraditi **besedišče** teh tokenov.\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": [
"Z uporabo besedišča lahko zlahka kodiramo našo tokenizirano niz v niz številk:\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": [
"## Predstavitev besedila z metodo Bag of Words\n",
"\n",
"Ker besede predstavljajo pomen, lahko včasih ugotovimo pomen besedila zgolj z opazovanjem posameznih besed, ne glede na njihov vrstni red v stavku. Na primer, pri razvrščanju novic besede, kot so *vreme* in *sneg*, verjetno nakazujejo na *vremensko napoved*, medtem ko besede, kot so *delnice* in *dolar*, kažejo na *finančne novice*.\n",
"\n",
"**Bag of Words** (BoW) je najpogosteje uporabljena tradicionalna predstavitev vektorskih podatkov. Vsaka beseda je povezana z indeksom vektorja, element vektorja pa vsebuje število pojavitev besede v določenem dokumentu.\n",
"\n",
"![Slika prikazuje, kako je predstavitev vektorja Bag of Words shranjena v pomnilniku.](../../../../../translated_images/bag-of-words-example.606fc1738f1d7ba98a9d693e3bcd706c6e83fa7bf8221e6e90d1a206d82f2ea4.sl.png) \n",
"\n",
"> **Note**: Na Bag of Words lahko gledate tudi kot na vsoto vseh vektorjev, kodiranih z metodo one-hot, za posamezne besede v besedilu.\n",
"\n",
"Spodaj je primer, kako ustvariti predstavitev Bag of Words z uporabo knjižnice Scikit Learn v Pythonu:\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": [
"Za izračun vektorja vreče besed iz vektorske predstavitve našega nabora podatkov AG_NEWS lahko uporabimo naslednjo funkcijo:\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": [
"> **Opomba:** Tukaj uporabljamo globalno spremenljivko `vocab_size` za določitev privzete velikosti besedišča. Ker je velikost besedišča pogosto precej velika, lahko omejimo velikost besedišča na najpogostejše besede. Poskusite znižati vrednost `vocab_size` in zagnati spodnjo kodo ter opazujte, kako to vpliva na natančnost. Pričakujete lahko nekaj zmanjšanja natančnosti, vendar ne dramatičnega, v zameno za boljšo zmogljivost.\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Učenje BoW klasifikatorja\n",
"\n",
"Zdaj, ko smo se naučili, kako zgraditi predstavitev Bag-of-Words za naš besedilni korpus, lahko na tej osnovi naučimo klasifikator. Najprej moramo naš nabor podatkov za učenje pretvoriti tako, da se vse pozicijske vektorske predstavitve spremenijo v predstavitev Bag-of-Words. To lahko dosežemo tako, da funkcijo `bowify` podamo kot parameter `collate_fn` standardnemu 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": [
"Zdaj definirajmo preprosto nevronsko mrežo za klasifikacijo, ki vsebuje eno linearno plast. Velikost vhodnega vektorja je enaka `vocab_size`, velikost izhoda pa ustreza številu razredov (4). Ker rešujemo nalogo klasifikacije, je končna aktivacijska funkcija `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": [
"Zdaj bomo definirali standardno učno zanko v PyTorchu. Ker je naš nabor podatkov precej velik, bomo za namen učenja trenirali le eno epoho, včasih pa celo manj kot eno epoho (določitev parametra `epoch_size` nam omogoča omejitev učenja). Prav tako bomo med učenjem poročali o akumulirani natančnosti učenja; pogostost poročanja je določena s parametrom `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": [
"## BiGrami, TriGrami in N-Grami\n",
"\n",
"Ena od omejitev pristopa vreče besed je, da so nekatere besede del večbesednih izrazov. Na primer, beseda 'hot dog' ima popolnoma drugačen pomen kot besedi 'hot' in 'dog' v drugih kontekstih. Če besedi 'hot' in 'dog' vedno predstavljamo z istimi vektorji, lahko to zmede naš model.\n",
"\n",
"Da bi to rešili, se pogosto uporabljajo **N-gramske reprezentacije** pri metodah za klasifikacijo dokumentov, kjer je frekvenca vsake besede, dvobesedne ali tribesedne kombinacije koristna značilnost za treniranje klasifikatorjev. Pri bigramski reprezentaciji, na primer, dodamo v besedišče vse pare besed poleg izvirnih besed.\n",
"\n",
"Spodaj je primer, kako ustvariti bigramsko vrečo besed z uporabo 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": [
"Glavna pomanjkljivost pristopa N-gram je, da velikost besedišča začne izjemno hitro naraščati. V praksi moramo združiti predstavitev N-gram z nekaterimi tehnikami za zmanjšanje dimenzionalnosti, kot so *vgnezditve* (embeddings), o katerih bomo govorili v naslednji enoti.\n",
"\n",
"Za uporabo predstavitve N-gram v našem naboru podatkov **AG News** moramo zgraditi posebno ngram besedišče:\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": [
"Nato bi lahko uporabili isto kodo kot zgoraj za treniranje klasifikatorja, vendar bi bila to zelo neučinkovita uporaba pomnilnika. V naslednji enoti bomo trenirali bigram klasifikator z uporabo vdelav.\n",
"\n",
"> **Opomba:** Pustite lahko samo tiste ngrame, ki se v besedilu pojavijo več kot določeno število krat. To bo zagotovilo, da bodo redki bigrami izpuščeni, in bo znatno zmanjšalo dimenzionalnost. Za to nastavite parameter `min_freq` na višjo vrednost in opazujte spremembo dolžine besedišča.\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Frekvenca izraza in obratna frekvenca dokumenta (TF-IDF)\n",
"\n",
"V predstavitvi BoW so pojavitve besed enakovredno ovrednotene, ne glede na samo besedo. Vendar je jasno, da so pogoste besede, kot so *a*, *in* itd., veliko manj pomembne za klasifikacijo kot specializirani izrazi. Pravzaprav so pri večini nalog NLP nekatere besede bolj relevantne kot druge.\n",
"\n",
"**TF-IDF** pomeni **frekvenca izrazaobratna frekvenca dokumenta**. Gre za različico vreče besed, kjer se namesto binarne vrednosti 0/1, ki označuje pojav besede v dokumentu, uporablja vrednost s plavajočo vejico, ki je povezana s frekvenco pojavljanja besede v korpusu.\n",
"\n",
"Bolj formalno je teža $w_{ij}$ besede $i$ v dokumentu $j$ definirana kot:\n",
"$$\n",
"w_{ij} = tf_{ij}\\times\\log({N\\over df_i})\n",
"$$\n",
"kjer\n",
"* $tf_{ij}$ predstavlja število pojavitev $i$ v $j$, torej vrednost BoW, ki smo jo videli prej\n",
"* $N$ je število dokumentov v zbirki\n",
"* $df_i$ je število dokumentov, ki vsebujejo besedo $i$ v celotni zbirki\n",
"\n",
"Vrednost TF-IDF $w_{ij}$ se povečuje sorazmerno s številom pojavitev besede v dokumentu in se zmanjša glede na število dokumentov v korpusu, ki vsebujejo to besedo, kar pomaga prilagoditi dejstvo, da se nekatere besede pojavljajo pogosteje kot druge. Na primer, če se beseda pojavi v *vsakem* dokumentu v zbirki, potem je $df_i=N$, in $w_{ij}=0$, takšni izrazi pa bi bili popolnoma prezrti.\n",
"\n",
"TF-IDF vektorizacijo besedila lahko enostavno ustvarite s pomočjo 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": [
"## Zaključek\n",
"\n",
"Čeprav TF-IDF predstavitve besedam dodelijo uteži glede na njihovo pogostost, ne morejo predstaviti pomena ali vrstnega reda. Kot je slavni jezikoslovec J. R. Firth leta 1935 dejal: \"Popoln pomen besede je vedno kontekstualen in nobena študija pomena brez konteksta ne more biti resna.\" Kasneje v tečaju se bomo naučili, kako zajeti kontekstualne informacije iz besedila z uporabo jezikovnega modeliranja.\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"\n---\n\n**Omejitev odgovornosti**: \nTa dokument je bil preveden z uporabo storitve za strojno prevajanje [Co-op Translator](https://github.com/Azure/co-op-translator). Čeprav si prizadevamo za natančnost, vas prosimo, da se zavedate, da lahko avtomatizirani prevodi vsebujejo napake ali netočnosti. Izvirni dokument v njegovem izvirnem jeziku je treba obravnavati kot avtoritativni vir. Za ključne informacije priporočamo strokovno človeško prevajanje. Ne prevzemamo odgovornosti za morebitna nesporazumevanja ali napačne razlage, ki izhajajo iz uporabe tega prevoda.\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-30T08:17:59+00:00",
"source_file": "lessons/5-NLP/13-TextRep/TextRepresentationPyTorch.ipynb",
"language_code": "sl"
}
},
"nbformat": 4,
"nbformat_minor": 2
}

View File

@ -0,0 +1,647 @@
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Naloga razvrščanja besedila\n",
"\n",
"V tem modulu bomo začeli s preprosto nalogo razvrščanja besedila na podlagi nabora podatkov **[AG_NEWS](http://www.di.unipi.it/~gulli/AG_corpus_of_news_articles.html)**: razvrstili bomo naslove novic v eno od 4 kategorij: Svet, Šport, Posel in Znanost/Tehnologija.\n",
"\n",
"## Nabor podatkov\n",
"\n",
"Za nalaganje nabora podatkov bomo uporabili API **[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": [
"Zdaj lahko dostopamo do učnih in testnih delov nabora podatkov z uporabo `dataset['train']` in `dataset['test']` oziroma:\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": [
"Natisnimo prvih 10 novih naslovov iz našega nabora podatkov:\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": [
"## Vektorizacija besedila\n",
"\n",
"Zdaj moramo besedilo pretvoriti v **številke**, ki jih lahko predstavimo kot tenzorje. Če želimo predstavitev na ravni besed, moramo narediti dve stvari:\n",
"\n",
"* Uporabiti **tokenizator**, da razdelimo besedilo na **tokene**.\n",
"* Zgraditi **besedišče** teh tokenov.\n",
"\n",
"### Omejevanje velikosti besedišča\n",
"\n",
"V primeru podatkovnega nabora AG News je velikost besedišča precej velika, več kot 100.000 besed. Na splošno ne potrebujemo besed, ki se redko pojavljajo v besedilu — le nekaj stavkov jih bo vsebovalo, in model se iz njih ne bo učil. Zato je smiselno omejiti velikost besedišča na manjše število z dodajanjem argumenta konstruktorju vektorizatorja:\n",
"\n",
"Oba koraka lahko izvedemo z uporabo sloja **TextVectorization**. Ustvarimo objekt vektorizatorja in nato pokličimo metodo `adapt`, da preletimo vse besedilo in zgradimo besedišče:\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": [
"> **Opomba** uporabljamo le podmnožico celotnega nabora podatkov za gradnjo besedišča. To počnemo, da pospešimo čas izvajanja in vam ni treba čakati. Vendar pa s tem tvegamo, da nekaterih besed iz celotnega nabora podatkov ne bo vključenih v besedišče in bodo med usposabljanjem prezrte. Zato bi uporaba celotne velikosti besedišča in obdelava celotnega nabora podatkov med `adapt` lahko povečala končno natančnost, vendar ne bistveno.\n",
"\n",
"Zdaj lahko dostopamo do dejanskega besedišča:\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": [
"Z uporabo vektorizatorja lahko enostavno kodiramo katero koli besedilo v niz številk:\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": [
"## Predstavitev besedila z metodo vreče besed\n",
"\n",
"Ker besede predstavljajo pomen, lahko včasih ugotovimo pomen določenega besedila že samo z opazovanjem posameznih besed, ne glede na njihov vrstni red v stavku. Na primer, pri razvrščanju novic besede, kot sta *vreme* in *sneg*, verjetno nakazujejo na *vremensko napoved*, medtem ko bi besede, kot sta *delnice* in *dolar*, kazale na *finančne novice*.\n",
"\n",
"**Predstavitev z metodo vreče besed** (BoW) je najpreprostejša za razumevanje med tradicionalnimi vektorskimi predstavitvami. Vsaka beseda je povezana z indeksom vektorja, element vektorja pa vsebuje število pojavitev posamezne besede v določenem dokumentu.\n",
"\n",
"![Slika, ki prikazuje, kako je predstavitev z metodo vreče besed predstavljena v pomnilniku.](../../../../../translated_images/bag-of-words-example.606fc1738f1d7ba98a9d693e3bcd706c6e83fa7bf8221e6e90d1a206d82f2ea4.sl.png) \n",
"\n",
"> **Opomba**: Na metodo BoW lahko gledate tudi kot na vsoto vseh eno-vroče kodiranih vektorjev za posamezne besede v besedilu.\n",
"\n",
"Spodaj je primer, kako ustvariti predstavitev z metodo vreče besed z uporabo knjižnice Scikit Learn v Pythonu:\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": [
"Prav tako lahko uporabimo Kerasov vektorizator, ki smo ga definirali zgoraj, vsako številko besede pretvorimo v eno-vroče kodiranje in vse te vektorje seštejemo:\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": [
"> **Opomba**: Morda vas bo presenetilo, da se rezultat razlikuje od prejšnjega primera. Razlog za to je, da v primeru s Keras dolžina vektorja ustreza velikosti besedišča, ki je bilo ustvarjeno iz celotnega nabora podatkov AG News, medtem ko smo v primeru s Scikit Learn besedišče ustvarili sproti iz vzorčnega besedila.\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Učenje klasifikatorja BoW\n",
"\n",
"Zdaj, ko smo se naučili, kako zgraditi predstavitev besedne vreče za našo besedilo, lahko začnemo z učenjem klasifikatorja, ki jo uporablja. Najprej moramo naš nabor podatkov pretvoriti v predstavitev besedne vreče. To lahko dosežemo z uporabo funkcije `map` na naslednji način:\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": [
"Zdaj definirajmo preprosto nevronsko mrežo za klasifikacijo, ki vsebuje eno linearno plast. Velikost vhodnega sloja je `vocab_size`, velikost izhodnega sloja pa ustreza številu razredov (4). Ker rešujemo nalogo klasifikacije, je končna aktivacijska funkcija **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": [
"Ker imamo 4 razrede, je natančnost nad 80 % dober rezultat.\n",
"\n",
"## Učenje klasifikatorja kot ene mreže\n",
"\n",
"Ker je vektorizator prav tako Kerasov sloj, lahko definiramo mrežo, ki ga vključuje, in jo učimo od začetka do konca. Na ta način nam ni treba vektorizirati nabora podatkov z uporabo `map`, temveč lahko prvotni nabor podatkov preprosto posredujemo vhodu mreže.\n",
"\n",
"> **Opomba**: Še vedno bi morali uporabiti `map` na našem naboru podatkov, da bi polja iz slovarjev (kot so `title`, `description` in `label`) pretvorili v torke. Vendar pa lahko ob nalaganju podatkov z diska že na začetku ustvarimo nabor podatkov z zahtevano strukturo.\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": [
"## Bigrami, trigrami in n-grami\n",
"\n",
"Ena od omejitev pristopa vreče besed je, da so nekatere besede del večbesednih izrazov. Na primer, beseda 'hot dog' ima popolnoma drugačen pomen kot besedi 'hot' in 'dog' v drugih kontekstih. Če besedi 'hot' in 'dog' vedno predstavljamo z istimi vektorji, lahko to zmede naš model.\n",
"\n",
"Da bi to rešili, se pogosto uporabljajo **n-gramske reprezentacije** pri metodah klasifikacije dokumentov, kjer je frekvenca vsake besede, dvobesedne ali tribesedne kombinacije koristna značilnost za učenje klasifikatorjev. Pri bigramskih reprezentacijah, na primer, dodamo vse pare besed v besedišče, poleg originalnih besed.\n",
"\n",
"Spodaj je primer, kako ustvariti bigramsko vrečo besed z uporabo 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": [
"Glavna pomanjkljivost pristopa n-gramov je, da se velikost besedišča začne izjemno hitro povečevati. V praksi moramo združiti predstavitev n-gramov s tehniko zmanjševanja dimenzionalnosti, kot so *vgnezditve* (embeddings), o katerih bomo govorili v naslednji enoti.\n",
"\n",
"Da uporabimo predstavitev n-gramov v našem **AG News** podatkovnem naboru, moramo parameter `ngrams` posredovati konstruktorju `TextVectorization`. Dolžina besedišča bigramov je **znatno večja**, v našem primeru več kot 1,3 milijona tokenov! Zato je smiselno omejiti tudi število bigramov na neko razumno vrednost.\n",
"\n",
"Lahko bi uporabili isto kodo kot zgoraj za treniranje klasifikatorja, vendar bi bila to zelo neučinkovita uporaba pomnilnika. V naslednji enoti bomo trenirali klasifikator bigramov z uporabo vgnezditve. Medtem pa lahko eksperimentirate s treniranjem klasifikatorja bigramov v tej beležnici in preverite, ali lahko dosežete višjo natančnost.\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Samodejno izračunavanje BoW vektorjev\n",
"\n",
"V zgornjem primeru smo BoW vektorje izračunali ročno, tako da smo sešteli enovrstne kodiranja posameznih besed. Vendar pa nam najnovejša različica TensorFlow omogoča samodejno izračunavanje BoW vektorjev, če parameter `output_mode='count` posredujemo konstruktorju vektorizatorja. To bistveno poenostavi definiranje in treniranje našega modela:\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": [
"## Frekvenca izraza - obratna frekvenca dokumenta (TF-IDF)\n",
"\n",
"V predstavitvi BoW so pojavitve besed utežene z enako tehniko, ne glede na samo besedo. Vendar je jasno, da so pogoste besede, kot sta *a* in *in*, veliko manj pomembne za klasifikacijo kot specializirani izrazi. Pri večini nalog obdelave naravnega jezika (NLP) so nekatere besede bolj relevantne kot druge.\n",
"\n",
"**TF-IDF** pomeni **frekvenca izraza - obratna frekvenca dokumenta**. Gre za različico metode bag-of-words, kjer namesto binarne vrednosti 0/1, ki označuje pojav besede v dokumentu, uporabimo številsko vrednost s plavajočo vejico, ki je povezana s frekvenco pojavljanja besede v korpusu.\n",
"\n",
"Bolj formalno je utež $w_{ij}$ besede $i$ v dokumentu $j$ definirana kot:\n",
"$$\n",
"w_{ij} = tf_{ij}\\times\\log({N\\over df_i})\n",
"$$\n",
"kjer\n",
"* $tf_{ij}$ predstavlja število pojavitev $i$ v $j$, torej vrednost BoW, ki smo jo videli prej\n",
"* $N$ je število dokumentov v zbirki\n",
"* $df_i$ je število dokumentov, ki vsebujejo besedo $i$ v celotni zbirki\n",
"\n",
"Vrednost TF-IDF $w_{ij}$ se povečuje sorazmerno s številom pojavitev besede v dokumentu in se zmanjša glede na število dokumentov v korpusu, ki vsebujejo to besedo. To pomaga prilagoditi dejstvo, da se nekatere besede pojavljajo pogosteje kot druge. Na primer, če se beseda pojavi v *vsakem* dokumentu v zbirki, potem velja $df_i=N$, in $w_{ij}=0$, kar pomeni, da so ti izrazi popolnoma zanemarjeni.\n",
"\n",
"TF-IDF vektorizacijo besedila lahko preprosto ustvarite s pomočjo 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": [
"V Kerasu lahko plast `TextVectorization` samodejno izračuna TF-IDF frekvence z uporabo parametra `output_mode='tf-idf'`. Ponovimo kodo, ki smo jo uporabili zgoraj, da vidimo, ali uporaba TF-IDF poveča natančnost:\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": [
"## Zaključek\n",
"\n",
"Čeprav TF-IDF predstavitve dodeljujejo uteži pogostosti različnim besedam, ne morejo predstaviti pomena ali vrstnega reda. Kot je slavni jezikoslovec J. R. Firth leta 1935 dejal: \"Popoln pomen besede je vedno kontekstualen, in nobena študija pomena brez konteksta ne more biti resna.\" Kasneje v tečaju se bomo naučili, kako zajeti kontekstualne informacije iz besedila z uporabo jezikovnega modeliranja.\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"\n---\n\n**Omejitev odgovornosti**: \nTa dokument je bil preveden z uporabo storitve za strojno prevajanje [Co-op Translator](https://github.com/Azure/co-op-translator). Čeprav si prizadevamo za natančnost, vas prosimo, da se zavedate, da lahko avtomatizirani prevodi vsebujejo napake ali netočnosti. Izvirni dokument v njegovem izvirnem jeziku je treba obravnavati kot avtoritativni vir. Za ključne informacije priporočamo strokovno človeško prevajanje. Ne prevzemamo odgovornosti za morebitna nesporazumevanja ali napačne razlage, ki izhajajo iz uporabe tega prevoda.\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-30T08:19:50+00:00",
"source_file": "lessons/5-NLP/13-TextRep/TextRepresentationTF.ipynb",
"language_code": "sl"
}
},
"nbformat": 4,
"nbformat_minor": 4
}

View File

@ -0,0 +1,726 @@
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Vdelave\n",
"\n",
"V našem prejšnjem primeru smo delali z visoko-dimenzionalnimi vektorji vreče besed dolžine `vocab_size`, pri čemer smo izrecno pretvarjali nizko-dimenzionalne vektorske predstavitve položajev v redke enovredne predstavitve. Ta enovredna predstavitev ni učinkovita glede pomnilnika, poleg tega pa se vsaka beseda obravnava neodvisno od drugih, tj. enovredni kodirani vektorji ne izražajo nobene semantične podobnosti med besedami.\n",
"\n",
"V tej enoti bomo nadaljevali z raziskovanjem podatkovne zbirke **News AG**. Za začetek naložimo podatke in pridobimo nekaj definicij iz prejšnjega zvezka.\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": [
"## Kaj je vgrajevanje?\n",
"\n",
"Ideja **vgrajevanja** je predstaviti besede z nižjedimenzionalnimi gostimi vektorji, ki nekako odražajo semantični pomen besede. Kasneje bomo razpravljali o tem, kako zgraditi smiselne vgrajene besede, vendar za zdaj razmišljajmo o vgrajevanju kot o načinu za zmanjšanje dimenzionalnosti vektorja besede.\n",
"\n",
"Torej, vgrajevalna plast bi kot vhod vzela besedo in proizvedla izhodni vektor določene velikosti `embedding_size`. Na nek način je zelo podobna plasti `Linear`, vendar namesto da bi vzela enovročno kodiran vektor, bo lahko kot vhod vzela številko besede.\n",
"\n",
"Z uporabo vgrajevalne plasti kot prve plasti v naši mreži lahko preklopimo iz modela vreče besed na model **vreče vgrajevanja**, kjer najprej vsako besedo v našem besedilu pretvorimo v ustrezno vgrajevanje, nato pa izračunamo neko agregatno funkcijo nad vsemi temi vgrajevanji, kot so `sum`, `average` ali `max`.\n",
"\n",
"![Slika, ki prikazuje klasifikator vgrajevanja za pet zaporednih besed.](../../../../../translated_images/embedding-classifier-example.b77f021a7ee67eeec8e68bfe11636c5b97d6eaa067515a129bfb1d0034b1ac5b.sl.png)\n",
"\n",
"Naša nevronska mreža klasifikatorja se bo začela z vgrajevalno plastjo, nato agregatno plastjo in linearnim klasifikatorjem na vrhu:\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": [
"### Obdelava spremenljive velikosti zaporedja\n",
"\n",
"Zaradi te arhitekture je treba minibatche za našo mrežo ustvariti na določen način. V prejšnji enoti, ko smo uporabljali vrečo besed (bag-of-words), so imeli vsi BoW tenzorji v minibatchu enako velikost `vocab_size`, ne glede na dejansko dolžino našega besedilnega zaporedja. Ko preidemo na vektorske predstavitve besed (word embeddings), bomo imeli različno število besed v vsakem vzorcu besedila, in pri združevanju teh vzorcev v minibatche bomo morali uporabiti nekaj oblazinjenja (padding).\n",
"\n",
"To lahko storimo z uporabo iste tehnike, kjer funkcijo `collate_fn` zagotovimo viru podatkov:\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": [
"### Usposabljanje klasifikatorja vdelav\n",
"\n",
"Zdaj, ko smo definirali ustrezen nalagalnik podatkov, lahko model usposobimo z uporabo funkcije za usposabljanje, ki smo jo definirali v prejšnji enoti:\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": [
"> **Opomba**: Tukaj treniramo samo za 25k zapisov (manj kot eno celotno epoho) zaradi prihranka časa, vendar lahko nadaljujete s treniranjem, napišete funkcijo za treniranje skozi več epoch in eksperimentirate s parametrom hitrosti učenja, da dosežete višjo natančnost. Morali bi doseči natančnost približno 90%.\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Plast EmbeddingBag in predstavitev zaporedij spremenljive dolžine\n",
"\n",
"V prejšnji arhitekturi smo morali vsa zaporedja zapolniti do enake dolžine, da so ustrezala mini seriji. To ni najbolj učinkovit način za predstavitev zaporedij spremenljive dolžine drugačen pristop bi bil uporaba **offset** vektorja, ki bi vseboval zamike vseh zaporedij, shranjenih v enem velikem vektorju.\n",
"\n",
"![Slika, ki prikazuje predstavitev zaporedij z zamiki](../../../../../translated_images/offset-sequence-representation.eb73fcefb29b46eecfbe74466077cfeb7c0f93a4f254850538a2efbc63517479.sl.png)\n",
"\n",
"> **Opomba**: Na zgornji sliki prikazujemo zaporedje znakov, vendar v našem primeru delamo z zaporedji besed. Kljub temu splošno načelo predstavitve zaporedij z offset vektorjem ostaja enako.\n",
"\n",
"Za delo s predstavitvijo z zamiki uporabljamo plast [`EmbeddingBag`](https://pytorch.org/docs/stable/generated/torch.nn.EmbeddingBag.html). Ta je podobna `Embedding`, vendar kot vhod prejme vsebinski vektor in offset vektor, poleg tega pa vključuje tudi plast za povprečenje, ki je lahko `mean`, `sum` ali `max`.\n",
"\n",
"Tukaj je spremenjeno omrežje, ki uporablja `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": [
"Za pripravo nabora podatkov za učenje moramo zagotoviti pretvorbeno funkcijo, ki bo pripravila vektorski premik:\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": [
"Upoštevajte, da za razliko od vseh prejšnjih primerov naša mreža zdaj sprejema dva parametra: podatkovni vektor in odmik vektor, ki sta različnih velikosti. Podobno nam naš nalagalnik podatkov zagotavlja 3 vrednosti namesto 2: tako besedilni kot odmikovni vektorji so zagotovljeni kot značilnosti. Zato moramo nekoliko prilagoditi našo funkcijo usposabljanja, da to upošteva:\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": [
"## Semantične vektorske predstavitve: Word2Vec\n",
"\n",
"V našem prejšnjem primeru se je plast za vektorsko predstavitev v modelu naučila preslikati besede v vektorsko obliko, vendar ta predstavitev ni imela veliko semantičnega pomena. Bilo bi koristno, če bi se naučili takšne vektorske predstavitve, kjer bi bile podobne besede ali sinonimi predstavljeni z vektorji, ki so si blizu glede na neko vektorsko razdaljo (npr. evklidska razdalja).\n",
"\n",
"Da to dosežemo, moramo naš model za vektorsko predstavitev predhodno naučiti na veliki zbirki besedil na specifičen način. Ena prvih metod za učenje semantičnih vektorskih predstavitev se imenuje [Word2Vec](https://en.wikipedia.org/wiki/Word2vec). Temelji na dveh glavnih arhitekturah, ki se uporabljata za ustvarjanje porazdeljene predstavitve besed:\n",
"\n",
" - **Neprekinjena vreča besed** (CBoW) — pri tej arhitekturi model učimo, da napove besedo iz okoliškega konteksta. Glede na n-gram $(W_{-2},W_{-1},W_0,W_1,W_2)$ je cilj modela napovedati $W_0$ na podlagi $(W_{-2},W_{-1},W_1,W_2)$.\n",
" - **Neprekinjeni preskok-gram** (skip-gram) je nasproten CBoW. Model uporablja okoliško okno kontekstnih besed za napoved trenutne besede.\n",
"\n",
"CBoW je hitrejši, medtem ko je skip-gram počasnejši, vendar bolje predstavlja redke besede.\n",
"\n",
"![Slika, ki prikazuje algoritma CBoW in Skip-Gram za pretvorbo besed v vektorje.](../../../../../translated_images/example-algorithms-for-converting-words-to-vectors.fbe9207a726922f6f0f5de66427e8a6eda63809356114e28fb1fa5f4a83ebda7.sl.png)\n",
"\n",
"Za eksperimentiranje z vektorsko predstavitvijo Word2Vec, predhodno naučeno na zbirki podatkov Google News, lahko uporabimo knjižnico **gensim**. Spodaj poiščemo besede, ki so najbolj podobne 'neural'.\n",
"\n",
"> **Opomba:** Ko prvič ustvarjate vektorske predstavitve besed, lahko prenos podatkov traja nekaj časa!\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": [
"Prav tako lahko izračunamo vektorske vdelave iz besede, ki jih uporabimo pri usposabljanju modela za klasifikacijo (za jasnost prikažemo le prvih 20 komponent vektorja):\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": [
"Odlična stvar semantičnih vdelav je, da lahko manipulirate z vektorskim kodiranjem, da spremenite semantiko. Na primer, lahko zahtevamo, da najdemo besedo, katere vektorska predstavitev bi bila čim bližje besedama *kralj* in *ženska*, ter čim bolj oddaljena od besede *moški*:\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": [
"Tako CBoW kot Skip-Grams sta \"prediktivni\" vektorski predstavitvi, saj upoštevata le lokalne kontekste. Word2Vec ne izkorišča globalnega konteksta.\n",
"\n",
"**FastText** nadgrajuje Word2Vec tako, da se nauči vektorske predstavitve za vsako besedo in za n-grame znakov, ki jih najdemo znotraj vsake besede. Vrednosti teh predstavitev se nato povprečijo v en vektor pri vsakem koraku učenja. Čeprav to doda veliko dodatnega računanja med predhodnim učenjem, omogoča, da vektorske predstavitve besed vključujejo informacije o podbesedah.\n",
"\n",
"Druga metoda, **GloVe**, izkorišča idejo matrike so-pojavitev in uporablja nevronske metode za razgradnjo matrike so-pojavitev v bolj izrazne in nelinearne vektorske predstavitve besed.\n",
"\n",
"Lahko se poigrate s primerom tako, da spremenite vektorske predstavitve v FastText in GloVe, saj gensim podpira več različnih modelov za vektorske predstavitve besed.\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Uporaba vnaprej naučenih vektorskih predstavitev v PyTorch\n",
"\n",
"Primer zgoraj lahko prilagodimo tako, da matriko v naši slojni predstavitvi (embedding layer) predhodno napolnimo s semantičnimi vektorskimi predstavitvami, kot je Word2Vec. Upoštevati moramo, da se besedišča vnaprej naučenih predstavitev in našega besedilnega korpusa verjetno ne bodo ujemala, zato bomo uteži za manjkajoče besede inicializirali z naključnimi vrednostmi:\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": [
"Zdaj trenirajmo naš model. Upoštevajte, da je čas, potreben za treniranje modela, bistveno daljši kot v prejšnjem primeru, zaradi večje velikosti vdelane plasti in posledično veliko večjega števila parametrov. Prav tako bomo morda morali model trenirati na več primerih, če želimo preprečiti prenaučenje.\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": [
"V našem primeru ne opazimo velikega povečanja natančnosti, kar je verjetno posledica precej različnih besedišč. \n",
"Da bi premagali težavo različnih besedišč, lahko uporabimo eno od naslednjih rešitev: \n",
"* Ponovno usposobimo model word2vec na našem besedišču \n",
"* Naložimo naš nabor podatkov z besediščem iz že usposobljenega modela word2vec. Besedišče, ki se uporablja za nalaganje nabora podatkov, je mogoče določiti med nalaganjem. \n",
"\n",
"Drugi pristop se zdi lažji, še posebej zato, ker PyTorch `torchtext` ogrodje vsebuje vgrajeno podporo za vektorske predstavitve besed. Na primer, lahko ustvarimo besedišče, ki temelji na GloVe, na naslednji način:\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": [
"Naloženi slovar ima naslednje osnovne operacije:\n",
"* Slovar `vocab.stoi` nam omogoča pretvorbo besede v njen indeks v slovarju\n",
"* `vocab.itos` opravlja nasprotno - pretvori številko v besedo\n",
"* `vocab.vectors` je matrika vektorskih predstavitev, tako da za pridobitev vektorske predstavitve besede `s` uporabimo `vocab.vectors[vocab.stoi[s]]`\n",
"\n",
"Tukaj je primer manipulacije vektorskih predstavitev, ki prikazuje enačbo **kind-man+woman = queen** (moral sem nekoliko prilagoditi koeficient, da deluje):\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": [
"Za učenje klasifikatorja z uporabo teh vdelav moramo najprej kodirati naš nabor podatkov z besediščem 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": [
"Kot smo videli zgoraj, so vsi vektorski vdelki shranjeni v matriki `vocab.vectors`. To omogoča zelo enostavno nalaganje teh uteži v uteži vdelane plasti z enostavnim kopiranjem:\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": [
"Zdaj trenirajmo naš model in preverimo, ali dobimo boljše rezultate:\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": [
"Eden od razlogov, zakaj ne opažamo znatnega povečanja natančnosti, je dejstvo, da nekaterih besed iz našega nabora podatkov ni v predhodno naučenem GloVe besedišču, zato so v bistvu prezrte. Da bi to premagali, lahko naučimo lastne vdelave na našem naboru podatkov.\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Kontekstualne vektorske predstavitve\n",
"\n",
"Ena ključnih omejitev tradicionalnih vnaprej naučenih vektorskih predstavitev, kot je Word2Vec, je problem razločevanja pomenov besed. Čeprav lahko vnaprej naučene vektorske predstavitve zajamejo del pomena besed v kontekstu, je vsak možen pomen besede kodiran v isti vektor. To lahko povzroči težave v nadaljnjih modelih, saj imajo številne besede, kot je na primer beseda 'play', različne pomene glede na kontekst, v katerem so uporabljene.\n",
"\n",
"Na primer, beseda 'play' ima v teh dveh stavkih precej različen pomen:\n",
"- Šel sem na **igro** v gledališče.\n",
"- John želi **igrati** s svojimi prijatelji.\n",
"\n",
"Vnaprej naučene vektorske predstavitve zgoraj predstavljajo oba pomena besede 'play' v istem vektorju. Da bi premagali to omejitev, moramo graditi vektorske predstavitve na podlagi **jezikovnega modela**, ki je naučen na velikem korpusu besedil in *ve*, kako se besede lahko povezujejo v različnih kontekstih. Razprava o kontekstualnih vektorskih predstavitvah presega okvir tega vodiča, vendar se bomo k njim vrnili, ko bomo govorili o jezikovnih modelih v naslednji enoti.\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"\n---\n\n**Omejitev odgovornosti**: \nTa dokument je bil preveden z uporabo storitve za strojno prevajanje [Co-op Translator](https://github.com/Azure/co-op-translator). Čeprav si prizadevamo za natančnost, vas prosimo, da upoštevate, da lahko avtomatizirani prevodi vsebujejo napake ali netočnosti. Izvirni dokument v njegovem izvirnem jeziku je treba obravnavati kot avtoritativni vir. Za ključne informacije priporočamo strokovno človeško prevajanje. Ne prevzemamo odgovornosti za morebitna nesporazumevanja ali napačne razlage, ki izhajajo iz uporabe tega prevoda.\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-30T08:16:17+00:00",
"source_file": "lessons/5-NLP/14-Embeddings/EmbeddingsPyTorch.ipynb",
"language_code": "sl"
}
},
"nbformat": 4,
"nbformat_minor": 2
}

View File

@ -0,0 +1,695 @@
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Vdelave\n",
"\n",
"V prejšnjem primeru smo delali z visoko-dimenzionalnimi vektorji vreče besed dolžine `vocab_size` in izrecno pretvorili nizko-dimenzionalne vektorske predstavitve položajev v redko enotočno predstavitev. Ta enotočna predstavitev ni pomnilniško učinkovita. Poleg tega se vsaka beseda obravnava neodvisno od drugih, zato enotočno kodirani vektorji ne izražajo semantičnih podobnosti med besedami.\n",
"\n",
"V tej enoti bomo nadaljevali raziskovanje podatkovne zbirke **News AG**. Za začetek naložimo podatke in pridobimo nekaj definicij iz prejšnje enote.\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": [
"### Kaj je vdelava?\n",
"\n",
"Ideja **vdelave** (embedding) je predstaviti besede z nižjedimenzionalnimi gostimi vektorji, ki odražajo semantični pomen besede. Kasneje bomo razpravljali o tem, kako zgraditi smiselne vdelave besed, za zdaj pa si vdelave predstavljajmo kot način za zmanjšanje dimenzionalnosti vektorja besede.\n",
"\n",
"Torej, vdelavni sloj prejme besedo kot vhod in ustvari izhodni vektor določene velikosti `embedding_size`. Na nek način je zelo podoben sloju `Dense`, vendar namesto da bi kot vhod prejel vektor z eno vročo kodiranjem (one-hot encoding), lahko sprejme številko besede.\n",
"\n",
"Z uporabo vdelavnega sloja kot prvega sloja v naši mreži lahko preklopimo iz modela vreče besed (bag-of-words) na model **vreče vdelav** (embedding bag), kjer najprej vsako besedo v našem besedilu pretvorimo v ustrezno vdelavo, nato pa izračunamo neko agregatno funkcijo nad vsemi temi vdelavami, na primer `sum`, `average` ali `max`.\n",
"\n",
"![Slika, ki prikazuje klasifikator z vdelavami za pet zaporednih besed.](../../../../../translated_images/embedding-classifier-example.b77f021a7ee67eeec8e68bfe11636c5b97d6eaa067515a129bfb1d0034b1ac5b.sl.png)\n",
"\n",
"Naša nevronska mreža za klasifikacijo je sestavljena iz naslednjih slojev:\n",
"\n",
"* Sloj `TextVectorization`, ki prejme niz kot vhod in ustvari tenzor številk tokenov. Določili bomo neko smiselno velikost besedišča `vocab_size` in ignorirali manj pogosto uporabljene besede. Vhodna oblika bo 1, izhodna oblika pa $n$, saj bomo kot rezultat dobili $n$ tokenov, pri čemer vsak vsebuje številke od 0 do `vocab_size`.\n",
"* Sloj `Embedding`, ki prejme $n$ številk in vsako številko zmanjša na gosti vektor določene dolžine (v našem primeru 100). Tako se vhodni tenzor oblike $n$ pretvori v tenzor oblike $n\\times 100$.\n",
"* Agregacijski sloj, ki izračuna povprečje tega tenzorja vzdolž prve osi, tj. izračuna povprečje vseh $n$ vhodnih tenzorjev, ki ustrezajo različnim besedam. Za implementacijo tega sloja bomo uporabili sloj `Lambda` in vanj posredovali funkcijo za izračun povprečja. Izhod bo imel obliko 100 in bo številska predstavitev celotnega vhodnega zaporedja.\n",
"* Končni linearni klasifikator `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": [
"V povzetku, v stolpcu **oblika izhoda**, prva dimenzija tenzorja `None` ustreza velikosti minibatcha, druga pa dolžini zaporedja tokenov. Vsa zaporedja tokenov v minibatchu imajo različne dolžine. O tem, kako se spopasti s tem, bomo razpravljali v naslednjem razdelku.\n",
"\n",
"Zdaj pa trenirajmo mrežo:\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": [
"> **Opomba**: gradimo vektorizator na podlagi podmnožice podatkov. To je storjeno za pospešitev procesa, kar lahko privede do situacije, ko vsi tokeni iz našega besedila niso prisotni v besedišču. V tem primeru bodo ti tokeni prezrti, kar lahko povzroči nekoliko nižjo natančnost. Vendar pa v resničnem življenju podmnožica besedila pogosto omogoča dobro oceno besedišča.\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Delo z različnimi dolžinami zaporedij spremenljivk\n",
"\n",
"Poglejmo, kako poteka učenje v mini serijah. V zgornjem primeru ima vhodni tenzor dimenzijo 1, uporabljamo pa mini serije dolžine 128, tako da je dejanska velikost tenzorja $128 \\times 1$. Vendar je število tokenov v vsakem stavku različno. Če uporabimo plast `TextVectorization` na en sam vhod, je število vrnjenih tokenov različno, odvisno od tega, kako je besedilo tokenizirano:\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": [
"Vendar, ko uporabimo vektorizator na več zaporedij, mora ustvariti tenzor pravokotne oblike, zato neuporabljene elemente zapolni z oznako PAD (ki je v našem primeru nič):\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": [
"Tukaj lahko vidimo vdelave:\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": [
"> **Opomba**: Da zmanjšamo količino dopolnjevanja, je v nekaterih primerih smiselno razvrstiti vse zaporedje v podatkovnem naboru po naraščajoči dolžini (ali, natančneje, po številu tokenov). To bo zagotovilo, da vsak minibatch vsebuje zaporedja podobne dolžine.\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Semantične vektorske predstavitve: Word2Vec\n",
"\n",
"V našem prejšnjem primeru se je vektorska plast naučila preslikati besede v vektorske predstavitve, vendar te predstavitve niso imele semantičnega pomena. Bilo bi koristno, če bi se naučili vektorske predstavitve, kjer bi bile podobne besede ali sinonimi predstavljeni z vektorji, ki so si blizu glede na neko vektorsko razdaljo (na primer evklidsko razdaljo).\n",
"\n",
"Da to dosežemo, moramo naš model za vektorske predstavitve predhodno naučiti na veliki zbirki besedil z uporabo tehnike, kot je [Word2Vec](https://en.wikipedia.org/wiki/Word2vec). Ta tehnika temelji na dveh glavnih arhitekturah, ki se uporabljata za ustvarjanje porazdeljenih predstavitev besed:\n",
"\n",
" - **Neprekinjena vreča besed** (CBoW), kjer model treniramo, da napove besedo na podlagi okoliškega konteksta. Glede na n-gram $(W_{-2},W_{-1},W_0,W_1,W_2)$ je cilj modela napovedati $W_0$ na podlagi $(W_{-2},W_{-1},W_1,W_2)$.\n",
" - **Neprekinjeni preskok-gram** (Continuous skip-gram) je nasproten CBoW. Model uporablja okoliško okno kontekstnih besed za napoved trenutne besede.\n",
"\n",
"CBoW je hitrejši, medtem ko je skip-gram počasnejši, vendar bolje predstavlja redke besede.\n",
"\n",
"![Slika, ki prikazuje algoritma CBoW in Skip-Gram za pretvorbo besed v vektorje.](../../../../../translated_images/example-algorithms-for-converting-words-to-vectors.fbe9207a726922f6f0f5de66427e8a6eda63809356114e28fb1fa5f4a83ebda7.sl.png)\n",
"\n",
"Za eksperimentiranje z Word2Vec vektorskimi predstavitvami, predhodno naučenimi na zbirki podatkov Google News, lahko uporabimo knjižnico **gensim**. Spodaj poiščemo besede, ki so najbolj podobne 'neural'.\n",
"\n",
"> **Opomba:** Ko prvič ustvarjate vektorske predstavitve besed, lahko prenos podatkov traja nekaj časa!\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": [
"Prav tako lahko iz besede pridobimo vektorsko vdelavo, ki jo uporabimo pri usposabljanju modela za klasifikacijo. Vdelava ima 300 komponent, vendar tukaj za jasnost prikažemo le prvih 20 komponent vektorja:\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": [
"Odlična stvar pri semantičnih vdelavah je, da lahko manipulirate z vektorskim kodiranjem na podlagi semantike. Na primer, lahko zahtevamo, da najdemo besedo, katere vektorska predstavitev je čim bližje besedama *kralj* in *ženska*, ter čim bolj oddaljena od besede *moški*:\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": [
"Zgornji primer uporablja nekaj notranje magije GenSym, vendar je osnovna logika pravzaprav precej preprosta. Zanimiva stvar pri vdelavah je, da lahko na vektorskih vdelavah izvajate običajne vektorske operacije, kar odraža operacije na pomenskih ravneh besed. Zgornji primer lahko izrazimo z vektorskimi operacijami: izračunamo vektor, ki ustreza **KRALJ-MOŠKI+ŽENSKA** (operaciji `+` in `-` se izvajata na vektorskih predstavitvah ustreznih besed), nato pa poiščemo najbližjo besedo v slovarju temu vektorju:\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": [
"> **OPOMBA**: Dodali smo majhne koeficiente k vektorjema *man* in *woman* poskusite jih odstraniti, da vidite, kaj se zgodi.\n",
"\n",
"Za iskanje najbližjega vektorja uporabimo TensorFlow orodja za izračun vektorja razdalj med našim vektorjem in vsemi vektorji v besedišču, nato pa z `argmin` poiščemo indeks najmanjše besede.\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Medtem ko se Word2Vec zdi odličen način za izražanje semantike besed, ima številne pomanjkljivosti, med drugim naslednje:\n",
"\n",
"* Tako modeli CBoW kot skip-gram so **prediktivni vektorji**, ki upoštevajo le lokalni kontekst. Word2Vec ne izkorišča globalnega konteksta.\n",
"* Word2Vec ne upošteva **morfologije** besed, tj. dejstva, da lahko pomen besede temelji na različnih delih besede, kot je koren.\n",
"\n",
"**FastText** poskuša premagati drugo omejitev in gradi na Word2Vec tako, da se uči vektorske predstavitve za vsako besedo in za n-grame znakov, ki jih najde znotraj vsake besede. Vrednosti teh predstavitev se nato povprečijo v en vektor pri vsakem koraku učenja. Čeprav to doda veliko dodatnega računanja pri predtreningu, omogoča vektorskim predstavitvam besed, da kodirajo informacije o podbesedah.\n",
"\n",
"Druga metoda, **GloVe**, uporablja drugačen pristop k vektorskim predstavitvam besed, ki temelji na faktorizaciji matrike beseda-kontekst. Najprej ustvari veliko matriko, ki šteje število pojavitev besed v različnih kontekstih, nato pa poskuša to matriko predstaviti v nižjih dimenzijah na način, ki minimizira izgubo rekonstrukcije.\n",
"\n",
"Knjižnica gensim podpira te vektorske predstavitve besed, in z njimi lahko eksperimentirate tako, da spremenite zgornjo kodo za nalaganje modela.\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Uporaba vnaprej naučenih vektorskih predstavitev v Kerasu\n",
"\n",
"Primer zgoraj lahko prilagodimo tako, da matriko v naši vektorski plasti vnaprej napolnimo s semantičnimi vektorskimi predstavitvami, kot je Word2Vec. Besedišči vnaprej naučenih vektorskih predstavitev in besedilnega korpusa se verjetno ne bosta ujemali, zato moramo izbrati eno. Tukaj raziskujemo dve možni možnosti: uporabo besedišča iz tokenizerja in uporabo besedišča iz Word2Vec predstavitev.\n",
"\n",
"### Uporaba besedišča iz tokenizerja\n",
"\n",
"Pri uporabi besedišča iz tokenizerja bodo nekatere besede iz besedišča imele ustrezne Word2Vec predstavitve, nekatere pa bodo manjkale. Glede na to, da je velikost našega besedišča `vocab_size`, dolžina vektorskih predstavitev Word2Vec pa `embed_size`, bo vektorska plast predstavljena z matriko uteži oblike `vocab_size`$\\times$`embed_size`. To matriko bomo napolnili tako, da bomo prešli skozi besedišče:\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": [
"Za besede, ki niso prisotne v Word2Vec besedišču, lahko pustimo vrednosti kot ničle ali pa ustvarimo naključni vektor.\n",
"\n",
"Zdaj lahko definiramo plast za vdelavo s predhodno naučenimi utežmi:\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": [
"> **Opomba**: Upoštevajte, da smo nastavili `trainable=False` pri ustvarjanju `Embedding`, kar pomeni, da sloja Embedding ne bomo ponovno trenirali. To lahko povzroči nekoliko nižjo natančnost, vendar pospeši proces učenja.\n",
"\n",
"### Uporaba besedišča za vdelave\n",
"\n",
"Ena težava pri prejšnjem pristopu je, da se besedišča, uporabljena v TextVectorization in Embedding, razlikujejo. Da bi rešili ta problem, lahko uporabimo eno od naslednjih rešitev:\n",
"* Ponovno treniramo model Word2Vec na našem besedišču.\n",
"* Naložimo naš nabor podatkov z besediščem iz vnaprej naučenega modela Word2Vec. Besedišča, uporabljena za nalaganje nabora podatkov, je mogoče določiti med nalaganjem.\n",
"\n",
"Drugi pristop se zdi enostavnejši, zato ga bomo implementirali. Najprej bomo ustvarili sloj `TextVectorization` z določenim besediščem, vzetim iz vdelav 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": [
"Knjižnica gensim za vektorske predstavitve besed vsebuje priročno funkcijo `get_keras_embeddings`, ki bo samodejno ustvarila ustrezno Kerasovo plast za vektorske predstavitve.\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": [
"Eden od razlogov, zakaj ne dosegamo večje natančnosti, je ta, da nekaterih besed iz našega nabora podatkov ni v predhodno naučenem GloVe besedišču, zato so v bistvu prezrte. Da bi to presegli, lahko naučimo lastne vdelave na podlagi našega nabora podatkov.\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Kontekstualne vektorske predstavitve\n",
"\n",
"Ena ključnih omejitev tradicionalnih vnaprej naučenih vektorskih predstavitev, kot je Word2Vec, je dejstvo, da kljub temu, da lahko zajamejo določen pomen besede, ne morejo razlikovati med različnimi pomeni. To lahko povzroči težave pri nadaljnjih modelih.\n",
"\n",
"Na primer, beseda 'play' ima različne pomene v teh dveh stavkih:\n",
"- Šel sem na **igro** v gledališče.\n",
"- John želi **igrati** s svojimi prijatelji.\n",
"\n",
"Vnaprej naučene vektorske predstavitve, o katerih smo govorili, predstavljajo oba pomena besede 'play' z isto vektorsko predstavitvijo. Da bi premagali to omejitev, moramo zgraditi vektorske predstavitve na podlagi **jezikovnega modela**, ki je naučen na velikem korpusu besedil in *ve*, kako se besede lahko povezujejo v različnih kontekstih. Razprava o kontekstualnih vektorskih predstavitvah presega okvir tega vodiča, vendar se bomo k njim vrnili, ko bomo govorili o jezikovnih modelih v naslednji enoti.\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"\n---\n\n**Omejitev odgovornosti**: \nTa dokument je bil preveden z uporabo storitve za strojno prevajanje [Co-op Translator](https://github.com/Azure/co-op-translator). Čeprav si prizadevamo za natančnost, vas prosimo, da upoštevate, da lahko avtomatizirani prevodi vsebujejo napake ali netočnosti. Izvirni dokument v njegovem izvirnem jeziku je treba obravnavati kot avtoritativni vir. Za ključne informacije priporočamo strokovno človeško prevajanje. Ne prevzemamo odgovornosti za morebitna nesporazumevanja ali napačne razlage, ki izhajajo iz uporabe tega prevoda.\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-30T08:14:01+00:00",
"source_file": "lessons/5-NLP/14-Embeddings/EmbeddingsTF.ipynb",
"language_code": "sl"
}
},
"nbformat": 4,
"nbformat_minor": 4
}

View File

@ -0,0 +1,576 @@
{
"cells": [
{
"cell_type": "markdown",
"metadata": {
"id": "NXTSugt6ieXh"
},
"source": [
"## Usposabljanje modela CBoW\n",
"\n",
"Ta zvezek je del [Učnega načrta za začetnike v AI](http://aka.ms/ai-beginners)\n",
"\n",
"V tem primeru bomo preučili usposabljanje jezikovnega modela CBoW za pridobitev lastnega vektorskega prostora Word2Vec. Kot vir besedila bomo uporabili podatkovno zbirko 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": [
"Najprej naložimo naš nabor podatkov in definiramo tokenizator ter besedišče. Nastavili bomo `vocab_size` na 5000, da nekoliko omejimo izračune.\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": [
"## Model CBoW\n",
"\n",
"CBoW se nauči napovedovati besedo na podlagi $2N$ sosednjih besed. Na primer, ko je $N=1$, dobimo naslednje pare iz stavka *I like to train networks*: (like,I), (I, like), (to, like), (like,to), (train,to), (to, train), (networks, train), (train,networks). Tukaj je prva beseda sosednja beseda, ki jo uporabimo kot vhod, druga beseda pa je tista, ki jo napovedujemo.\n",
"\n",
"Za izdelavo mreže, ki napoveduje naslednjo besedo, moramo sosednjo besedo podati kot vhod in dobiti številko besede kot izhod. Arhitektura mreže CBoW je naslednja:\n",
"\n",
"* Vhodna beseda se prenese skozi plast vgrajevanja. Ta plast vgrajevanja bo naša vgrajevanje Word2Vec, zato jo bomo definirali ločeno kot spremenljivko `embedder`. V tem primeru bomo uporabili velikost vgrajevanja = 30, čeprav bi morda želeli eksperimentirati z večjimi dimenzijami (pravi Word2Vec ima 300).\n",
"* Vektor vgrajevanja se nato prenese skozi linearno plast, ki bo napovedala izhodno besedo. Zato ima `vocab_size` nevronov.\n",
"\n",
"Za izhod, če uporabimo `CrossEntropyLoss` kot funkcijo izgube, moramo podati le številke besed kot pričakovane rezultate, brez enovektorskega kodiranja.\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": [
"## Priprava podatkov za učenje\n",
"\n",
"Zdaj bomo napisali glavno funkcijo, ki bo iz besedila izračunala CBoW pare besed. Ta funkcija nam bo omogočila določiti velikost okna in bo vrnila niz parov - vhodno in izhodno besedo. Upoštevajte, da se ta funkcija lahko uporablja tako na besedah kot na vektorjih/tensorjih - kar nam bo omogočilo kodiranje besedila, preden ga posredujemo funkciji `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": [
"Pripravimo učni nabor podatkov. Pregledali bomo vse novice, poklicali `to_cbow`, da dobimo seznam parov besed, in te pare dodali v `X` in `Y`. Zaradi prihranka časa bomo upoštevali le prvih 10 tisoč novic - omejitev lahko enostavno odstranite, če imate več časa za čakanje in želite dobiti boljše vdelave :)\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": [
"Podatke bomo prav tako pretvorili v en nabor podatkov in ustvarili nalagalnik podatkov:\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": [
"Prav tako bomo te podatke pretvorili v en nabor podatkov in ustvarili nalagalnik podatkov:\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": [
"Zdaj začnimo s pravim treningom. Uporabili bomo optimizator `SGD` z dokaj visoko hitrostjo učenja. Lahko poskusite tudi z drugimi optimizatorji, kot je `Adam`. Za začetek bomo trenirali 10 epoh - to celico lahko ponovno zaženete, če želite še nižjo izgubo.\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": [
"## Preizkušanje Word2Vec\n",
"\n",
"Za uporabo Word2Vec izvlecimo vektorje, ki ustrezajo vsem besedam v našem besedišču:\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": [
"Poglejmo na primer, kako je beseda **Pariz** kodirana v vektor:\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": [
"Zanimivo je uporabiti Word2Vec za iskanje sinonimov. Naslednja funkcija bo vrnila `n` najbližjih besed glede na podan vhod. Da jih najdemo, izračunamo normo $|w_i - v|$, kjer je $v$ vektor, ki ustreza naši vhodni besedi, in $w_i$ je kodiranje $i$-te besede v besedišču. Nato razvrstimo tabelo in vrnemo ustrezne indekse z uporabo `argsort`, ter vzamemo prvih `n` elementov seznama, ki kodirajo položaje najbližjih besed v besedišču.\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": [
"## Ključne točke\n",
"\n",
"Z uporabo pametnih tehnik, kot je CBoW, lahko treniramo model Word2Vec. Prav tako lahko poskusite trenirati model skip-gram, ki je zasnovan za napovedovanje sosednje besede glede na osrednjo, in preverite, kako dobro deluje.\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"\n---\n\n**Omejitev odgovornosti**: \nTa dokument je bil preveden z uporabo storitve za strojno prevajanje [Co-op Translator](https://github.com/Azure/co-op-translator). Čeprav si prizadevamo za natančnost, vas opozarjamo, da lahko avtomatizirani prevodi vsebujejo napake ali netočnosti. Izvirni dokument v njegovem izvirnem jeziku je treba obravnavati kot avtoritativni vir. Za ključne informacije priporočamo strokovno človeško prevajanje. Ne prevzemamo odgovornosti za morebitna nesporazumevanja ali napačne razlage, ki bi izhajale iz uporabe tega prevoda.\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-30T08:02:02+00:00",
"source_file": "lessons/5-NLP/15-LanguageModeling/CBoW-PyTorch.ipynb",
"language_code": "sl"
}
},
"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": [
"# Rekurentne nevronske mreže\n",
"\n",
"V prejšnjem modulu smo uporabljali bogate semantične reprezentacije besedila in preprost linearni klasifikator na vrhu vgrajenih predstavitev. Ta arhitektura zajame združeni pomen besed v stavku, vendar ne upošteva **vrstnega reda** besed, saj operacija združevanja na vrhu vgrajenih predstavitev odstrani to informacijo iz izvirnega besedila. Ker ti modeli ne morejo modelirati vrstnega reda besed, ne morejo reševati bolj zapletenih ali dvoumnih nalog, kot so generiranje besedila ali odgovarjanje na vprašanja.\n",
"\n",
"Da bi zajeli pomen zaporedja besedila, moramo uporabiti drugo arhitekturo nevronske mreže, imenovano **rekurentna nevronska mreža** ali RNN. Pri RNN stavke pošiljamo skozi mrežo en simbol naenkrat, mreža pa ustvari neko **stanje**, ki ga nato skupaj z naslednjim simbolom ponovno pošljemo v mrežo.\n",
"\n",
"Glede na vhodno zaporedje tokenov $X_0,\\dots,X_n$, RNN ustvari zaporedje blokov nevronske mreže in to zaporedje trenira od začetka do konca z uporabo povratnega razširjanja napake. Vsak blok mreže sprejme par $(X_i,S_i)$ kot vhod in ustvari $S_{i+1}$ kot rezultat. Končno stanje $S_n$ ali izhod $X_n$ gre v linearni klasifikator, da ustvari rezultat. Vsi bloki mreže imajo enake uteži in se trenirajo od začetka do konca z enim prehodom povratnega razširjanja napake.\n",
"\n",
"Ker se vektorska stanja $S_0,\\dots,S_n$ prenašajo skozi mrežo, lahko ta model uči zaporedne odvisnosti med besedami. Na primer, ko se beseda *ne* pojavi nekje v zaporedju, se lahko nauči negirati določene elemente znotraj vektorskega stanja, kar vodi do negacije.\n",
"\n",
"> Ker so uteži vseh blokov RNN na sliki enake, lahko isto sliko predstavimo kot en blok (na desni) z povratno zanko, ki prenaša izhodno stanje mreže nazaj na vhod.\n",
"\n",
"Poglejmo, kako nam lahko rekurentne nevronske mreže pomagajo pri klasifikaciji našega nabora novic.\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": [
"## Preprost RNN klasifikator\n",
"\n",
"V primeru preprostega RNN je vsaka rekurentna enota preprosto linearno omrežje, ki sprejme združen vhodni vektor in vektorsko stanje ter ustvari novo vektorsko stanje. PyTorch predstavlja to enoto z razredom `RNNCell`, omrežje takšnih celic pa kot plast `RNN`.\n",
"\n",
"Za definiranje RNN klasifikatorja bomo najprej uporabili vgradno plast (embedding layer), da zmanjšamo dimenzionalnost vhodnega besedišča, nato pa bomo na vrhu dodali plast 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": [
"> **Opomba:** Tukaj uporabljamo neizurjeno vgrajeno plast za enostavnost, vendar lahko za še boljše rezultate uporabimo vnaprej izurjeno vgrajeno plast z Word2Vec ali GloVe vgraditvami, kot je opisano v prejšnji enoti. Za boljše razumevanje lahko ta koda prilagodite tako, da deluje z vnaprej izurjenimi vgraditvami.\n",
"\n",
"V našem primeru bomo uporabili podajalnik podatkov z izravnavo (padded data loader), tako da bo vsak paket vseboval več izravnanih zaporedij enake dolžine. RNN plast bo vzela zaporedje vgraditvenih tenzorjev in ustvarila dva izhoda:\n",
"* $x$ je zaporedje izhodov RNN celic na vsakem koraku\n",
"* $h$ je končno skrito stanje za zadnji element zaporedja\n",
"\n",
"Nato uporabimo popolnoma povezani linearni klasifikator, da dobimo število razredov.\n",
"\n",
"> **Opomba:** RNN-ji so precej zahtevni za učenje, saj je število plasti, vključenih v povratno propagacijo, precej veliko, ko so RNN celice razširjene vzdolž dolžine zaporedja. Zato moramo izbrati majhno hitrost učenja in mrežo učiti na večjem naboru podatkov, da dosežemo dobre rezultate. To lahko traja precej dolgo, zato je priporočljiva uporaba 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": [
"## Dolgoročni kratkoročni spomin (LSTM)\n",
"\n",
"Ena glavnih težav klasičnih RNN-jev je tako imenovana težava **izginjajočih gradientov**. Ker se RNN-ji učijo od začetka do konca v enem prehodu z vzvratnim razširjanjem, imajo težave s prenašanjem napake do prvih slojev mreže, zaradi česar mreža ne more učiti odnosov med oddaljenimi tokeni. Eden od načinov za izogibanje tej težavi je uvedba **eksplicitnega upravljanja stanja** z uporabo tako imenovanih **vrat**. Dve najbolj znani arhitekturi te vrste sta: **Dolgoročni kratkoročni spomin** (LSTM) in **Enota z vrati za posredovanje** (GRU).\n",
"\n",
"![Slika, ki prikazuje primer celice dolgoročnega kratkoročnega spomina](../../../../../lessons/5-NLP/16-RNN/images/long-short-term-memory-cell.svg)\n",
"\n",
"LSTM mreža je organizirana na način, podoben RNN-ju, vendar se iz sloja v sloj prenašata dve stanji: dejansko stanje $c$ in skriti vektor $h$. Pri vsaki enoti se skriti vektor $h_i$ združi z vhodom $x_i$, in skupaj nadzorujeta, kaj se zgodi s stanjem $c$ prek **vrat**. Vsaka vrata so nevronska mreža s sigmoidno aktivacijo (izhod v območju $[0,1]$), ki jih lahko razumemo kot bitno masko, ko jih pomnožimo z vektorskim stanjem. Obstajajo naslednja vrata (od leve proti desni na zgornji sliki):\n",
"* **vrata za pozabo** vzamejo skriti vektor in določijo, katere komponente vektorja $c$ moramo pozabiti in katere prenesti naprej.\n",
"* **vhodna vrata** vzamejo nekaj informacij iz vhoda in skritega vektorja ter jih vstavijo v stanje.\n",
"* **izhodna vrata** transformirajo stanje prek neke linearne plasti s $\\tanh$ aktivacijo, nato pa izberejo nekatere njegove komponente z uporabo skritega vektorja $h_i$, da ustvarijo novo stanje $c_{i+1}$.\n",
"\n",
"Komponente stanja $c$ lahko razumemo kot neke vrste zastavice, ki jih lahko vklopimo ali izklopimo. Na primer, ko v zaporedju naletimo na ime *Alice*, lahko predpostavimo, da se nanaša na ženski lik, in dvignemo zastavico v stanju, da imamo v stavku ženski samostalnik. Ko kasneje naletimo na frazo *in Tom*, bomo dvignili zastavico, da imamo množinski samostalnik. Tako lahko z manipulacijo stanja domnevno sledimo slovničnim lastnostim delov stavka.\n",
"\n",
"> **Opomba**: Odličen vir za razumevanje notranje strukture LSTM je ta odlični članek [Understanding LSTM Networks](https://colah.github.io/posts/2015-08-Understanding-LSTMs/) avtorja Christopherja Olaha.\n",
"\n",
"Čeprav se notranja struktura LSTM celice morda zdi zapletena, PyTorch to implementacijo skriva znotraj razreda `LSTMCell` in ponuja objekt `LSTM` za predstavitev celotnega LSTM sloja. Tako bo implementacija LSTM klasifikatorja precej podobna preprostemu RNN-ju, ki smo ga videli zgoraj:\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": [
"## Zapakirane sekvence\n",
"\n",
"V našem primeru smo morali vse sekvence v mini seriji zapolniti z ničelnimi vektorji. Čeprav to povzroči nekaj nepotrebne porabe pomnilnika, je pri RNN-jih še bolj kritično, da se ustvarijo dodatne RNN celice za zapolnjene vnose, ki sodelujejo pri učenju, vendar ne nosijo nobenih pomembnih vhodnih informacij. Veliko bolje bi bilo, če bi RNN trenirali le do dejanske dolžine sekvence.\n",
"\n",
"Za to je v PyTorch uveden poseben format za shranjevanje zapolnjenih sekvenc. Predpostavimo, da imamo vhodno zapolnjeno mini serijo, ki izgleda takole:\n",
"```\n",
"[[1,2,3,4,5],\n",
" [6,7,8,0,0],\n",
" [9,0,0,0,0]]\n",
"```\n",
"Tukaj 0 predstavlja zapolnjene vrednosti, dejanski vektor dolžin vhodnih sekvenc pa je `[5,3,1]`.\n",
"\n",
"Da bi učinkovito trenirali RNN z zapolnjenimi sekvencami, želimo začeti treniranje prve skupine RNN celic z veliko mini serijo (`[1,6,9]`), nato pa zaključiti obdelavo tretje sekvence in nadaljevati treniranje s krajšimi mini serijami (`[2,7]`, `[3,8]`) in tako naprej. Tako je zapakirana sekvenca predstavljena kot en vektor - v našem primeru `[1,6,9,2,7,3,8,4,5]`, in vektor dolžin (`[5,3,1]`), iz katerega lahko enostavno rekonstruiramo izvirno zapolnjeno mini serijo.\n",
"\n",
"Za ustvarjanje zapakirane sekvence lahko uporabimo funkcijo `torch.nn.utils.rnn.pack_padded_sequence`. Vse rekurentne plasti, vključno z RNN, LSTM in GRU, podpirajo zapakirane sekvence kot vhod in proizvajajo zapakiran izhod, ki ga lahko dekodiramo z uporabo `torch.nn.utils.rnn.pad_packed_sequence`.\n",
"\n",
"Da bi lahko ustvarili zapakirano sekvenco, moramo omrežju posredovati vektor dolžin, zato potrebujemo drugačno funkcijo za pripravo mini serij:\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": [
"Dejansko bo mreža zelo podobna `LSTMClassifier` zgoraj, vendar bo metoda `forward` prejela tako zapolnjen mini-sklop kot tudi vektor dolžin zaporedij. Po izračunu vdelave izračunamo zapakiran niz, ga posredujemo sloju LSTM in nato rezultat ponovno razpakiramo.\n",
"\n",
"> **Opomba**: Pravzaprav razpakiranega rezultata `x` ne uporabljamo, saj v nadaljnjih izračunih uporabljamo izhod iz skritih slojev. Zato lahko razpakiranje v tej kodi popolnoma odstranimo. Razlog, da ga tukaj vključimo, je, da vam omogočimo enostavno spreminjanje te kode, če bi morali uporabiti izhod mreže v nadaljnjih izračunih.\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": [
"> **Opomba:** Morda ste opazili parameter `use_pack_sequence`, ki ga posredujemo funkciji za učenje. Trenutno funkcija `pack_padded_sequence` zahteva, da je dolžinski zaporedni tenzor na napravi CPU, zato mora funkcija za učenje preprečiti prenos podatkov o dolžinskem zaporedju na GPU med učenjem. Implementacijo funkcije `train_emb` si lahko ogledate v datoteki [`torchnlp.py`](../../../../../lessons/5-NLP/16-RNN/torchnlp.py).\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Dvosmerne in večplastne RNN\n",
"\n",
"V naših primerih so vse rekurzivne mreže delovale v eni smeri, od začetka zaporedja do konca. To se zdi naravno, saj spominja na način, kako beremo in poslušamo govor. Vendar pa, ker imamo v mnogih praktičnih primerih naključni dostop do vhodnega zaporedja, bi bilo smiselno izvajati rekurzivno računanje v obeh smereh. Takšne mreže imenujemo **dvosmerne** RNN, in jih lahko ustvarimo z dodajanjem parametra `bidirectional=True` konstruktorju RNN/LSTM/GRU.\n",
"\n",
"Pri delu z dvosmerno mrežo potrebujemo dva vektorja skritega stanja, enega za vsako smer. PyTorch te vektorje kodira kot en vektor z dvakrat večjo velikostjo, kar je precej priročno, saj običajno posredujemo nastalo skrito stanje v popolnoma povezano linearno plast, pri čemer moramo le upoštevati to povečanje velikosti pri ustvarjanju plasti.\n",
"\n",
"Rekurzivna mreža, enosmerna ali dvosmerna, zajame določene vzorce znotraj zaporedja in jih lahko shrani v vektor stanja ali prenese v izhod. Tako kot pri konvolucijskih mrežah lahko na prvo plast zgradimo drugo rekurzivno plast, da zajamemo vzorce višje ravni, ki so zgrajeni iz vzorcev nižje ravni, ki jih je izločila prva plast. To nas pripelje do pojma **večplastne RNN**, ki je sestavljena iz dveh ali več rekurzivnih mrež, kjer se izhod prejšnje plasti prenese v naslednjo plast kot vhod.\n",
"\n",
"![Slika, ki prikazuje večplastno dolgotrajno-kratkoročno pomnilniško RNN](../../../../../translated_images/multi-layer-lstm.dd975e29bb2a59fe58b429db833932d734c81f211cad2783797a9608984acb8c.sl.jpg)\n",
"\n",
"*Slika iz [tega čudovitega prispevka](https://towardsdatascience.com/from-a-lstm-cell-to-a-multilayer-lstm-network-with-pytorch-2899eb5696f3) avtorja Fernanda Lópeza*\n",
"\n",
"PyTorch omogoča enostavno konstrukcijo takšnih mrež, saj morate le dodati parameter `num_layers` konstruktorju RNN/LSTM/GRU, da samodejno zgradite več plasti rekurzije. To pa tudi pomeni, da se velikost skritega/stanja vektorja sorazmerno poveča, kar morate upoštevati pri obdelavi izhoda rekurzivnih plasti.\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## RNN-ji za druge naloge\n",
"\n",
"V tej enoti smo videli, da se RNN-ji lahko uporabljajo za klasifikacijo sekvenc, vendar dejansko lahko obravnavajo še veliko več nalog, kot so generiranje besedila, strojno prevajanje in druge. Te naloge bomo obravnavali v naslednji enoti.\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"\n---\n\n**Omejitev odgovornosti**: \nTa dokument je bil preveden z uporabo storitve za strojno prevajanje [Co-op Translator](https://github.com/Azure/co-op-translator). Čeprav si prizadevamo za natančnost, vas prosimo, da upoštevate, da lahko avtomatizirani prevodi vsebujejo napake ali netočnosti. Izvirni dokument v njegovem izvirnem jeziku je treba obravnavati kot avtoritativni vir. Za ključne informacije priporočamo strokovno človeško prevajanje. Ne prevzemamo odgovornosti za morebitna nesporazumevanja ali napačne razlage, ki izhajajo iz uporabe tega prevoda.\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-30T08:11:38+00:00",
"source_file": "lessons/5-NLP/16-RNN/RNNPyTorch.ipynb",
"language_code": "sl"
}
},
"nbformat": 4,
"nbformat_minor": 2
}

View File

@ -0,0 +1,460 @@
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Rekurentne nevronske mreže\n",
"\n",
"V prejšnjem modulu smo obravnavali bogate semantične reprezentacije besedila. Arhitektura, ki smo jo uporabljali, zajame združeni pomen besed v stavku, vendar ne upošteva **vrstnega reda** besed, saj operacija združevanja, ki sledi vdelavam, odstrani to informacijo iz izvirnega besedila. Ker ti modeli ne morejo predstavljati vrstnega reda besed, ne morejo reševati bolj zapletenih ali dvoumnih nalog, kot sta generiranje besedila ali odgovarjanje na vprašanja.\n",
"\n",
"Za zajemanje pomena zaporedja besedila bomo uporabili arhitekturo nevronske mreže, imenovano **rekurentna nevronska mreža** ali RNN. Pri uporabi RNN stavke pošiljamo skozi mrežo en token naenkrat, mreža pa ustvari neko **stanje**, ki ga nato skupaj z naslednjim tokenom ponovno pošljemo v mrežo.\n",
"\n",
"![Slika, ki prikazuje primer generiranja z rekurentno nevronsko mrežo.](../../../../../translated_images/rnn.27f5c29c53d727b546ad3961637a267f0fe9ec5ab01f2a26a853c92fcefbb574.sl.png)\n",
"\n",
"Glede na vhodno zaporedje tokenov $X_0,\\dots,X_n$ RNN ustvari zaporedje blokov nevronske mreže in to zaporedje trenira od začetka do konca z uporabo povratnega razširjanja napake (backpropagation). Vsak blok mreže kot vhod prejme par $(X_i,S_i)$ in kot rezultat ustvari $S_{i+1}$. Končno stanje $S_n$ ali izhod $Y_n$ gre v linearni klasifikator, da ustvari rezultat. Vsi bloki mreže si delijo iste uteži in so trenirani od začetka do konca z enim prehodom povratnega razširjanja napake.\n",
"\n",
"> Zgornja slika prikazuje rekurentno nevronsko mrežo v razširjeni obliki (na levi) in v bolj kompaktni rekurentni predstavitvi (na desni). Pomembno je razumeti, da imajo vse RNN celice iste **deljive uteži**.\n",
"\n",
"Ker se vektorska stanja $S_0,\\dots,S_n$ prenašajo skozi mrežo, lahko RNN uči zaporedne odvisnosti med besedami. Na primer, ko se beseda *ne* pojavi nekje v zaporedju, se lahko nauči negirati določene elemente znotraj vektorskega stanja.\n",
"\n",
"Vsaka RNN celica vsebuje dve matriki uteži: $W_H$ in $W_I$, ter pristranskost $b$. Na vsakem koraku RNN se glede na vhod $X_i$ in vhodno stanje $S_i$ izhodno stanje izračuna kot $S_{i+1} = f(W_H\\times S_i + W_I\\times X_i+b)$, kjer je $f$ aktivacijska funkcija (pogosto $\\tanh$).\n",
"\n",
"> Pri težavah, kot sta generiranje besedila (ki ga bomo obravnavali v naslednji enoti) ali strojno prevajanje, želimo dobiti tudi neko izhodno vrednost na vsakem koraku RNN. V tem primeru obstaja še ena matrika $W_O$, izhod pa se izračuna kot $Y_i=f(W_O\\times S_i+b_O)$.\n",
"\n",
"Poglejmo, kako nam lahko rekurentne nevronske mreže pomagajo pri klasifikaciji našega nabora novic.\n",
"\n",
"> Za peskovnik okolje moramo zagnati naslednjo celico, da zagotovimo, da je potrebna knjižnica nameščena in da so podatki prednaloženi. Če izvajate lokalno, lahko naslednjo celico preskočite.\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": [
"Ko treniramo velike modele, lahko razporeditev pomnilnika na GPU postane težava. Prav tako moramo eksperimentirati z različnimi velikostmi minibatch, da podatki ustrezajo pomnilniku GPU, hkrati pa je trening dovolj hiter. Če to kodo izvajate na svojem GPU stroju, lahko eksperimentirate z nastavitvijo velikosti minibatch, da pospešite trening.\n",
"\n",
"> **Opomba**: Znano je, da določene različice NVidia gonilnikov po treniranju modela ne sprostijo pomnilnika. V teh zvezkih izvajamo več primerov, kar lahko povzroči izčrpanje pomnilnika v določenih nastavitvah, še posebej, če izvajate lastne eksperimente v istem zvezku. Če naletite na nenavadne napake ob začetku treniranja modela, boste morda morali znova zagnati jedro zvezka.\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": [
"## Preprost RNN klasifikator\n",
"\n",
"V primeru preprostega RNN je vsaka rekurentna enota preprosta linearna mreža, ki sprejme vhodni vektor in vektorsko stanje ter ustvari novo vektorsko stanje. V Kerasu to lahko predstavimo s plastjo `SimpleRNN`.\n",
"\n",
"Čeprav lahko RNN plasti neposredno posredujemo eno-vroče kodirane tokene, to ni dobra ideja zaradi njihove visoke dimenzionalnosti. Zato bomo uporabili vgradno plast (embedding layer), da zmanjšamo dimenzionalnost vektorskih predstav besed, nato pa plast RNN in na koncu klasifikator `Dense`.\n",
"\n",
"> **Opomba**: V primerih, ko dimenzionalnost ni tako visoka, na primer pri uporabi tokenizacije na ravni znakov, bi bilo smiselno eno-vroče kodirane tokene neposredno posredovati celici 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": [
"> **Opomba:** Tukaj uporabljamo neizurjeno vgrajeno plast za enostavnost, vendar za boljše rezultate lahko uporabimo vnaprej izurjeno vgrajeno plast z uporabo Word2Vec, kot je opisano v prejšnji enoti. Dobra vaja bi bila prilagoditi to kodo za delo z vnaprej izurjenimi vgrajenimi plastmi.\n",
"\n",
"Zdaj bomo izurili naš RNN. RNN-ji so na splošno precej težavni za učenje, saj je število plasti, ki sodelujejo pri povratnem razširjanju napake, zelo veliko, ko se celice RNN razširijo vzdolž dolžine zaporedja. Zato moramo izbrati manjšo stopnjo učenja in mrežo izuriti na večjem naboru podatkov, da dosežemo dobre rezultate. To lahko traja precej dolgo, zato je priporočljiva uporaba GPU-ja.\n",
"\n",
"Da pospešimo proces, bomo model RNN izurili samo na naslovih novic in izpustili opise. Lahko poskusite z učenjem na opisih in preverite, ali lahko model izurite.\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": [
"> **Opomba** da je natančnost tukaj verjetno nižja, ker treniramo samo na naslovih novic.\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Ponovno preučimo zaporedja spremenljivk\n",
"\n",
"Ne pozabite, da bo plast `TextVectorization` samodejno dopolnila zaporedja spremenljive dolžine v mini seriji z zapolnitvenimi žetoni. Izkazalo se je, da ti žetoni sodelujejo tudi pri učenju, kar lahko oteži konvergenco modela.\n",
"\n",
"Obstaja več pristopov, ki jih lahko uporabimo za zmanjšanje količine zapolnitve. Eden od njih je preurejanje nabora podatkov glede na dolžino zaporedja in združevanje vseh zaporedij po velikosti. To lahko storimo z uporabo funkcije `tf.data.experimental.bucket_by_sequence_length` (glejte [dokumentacijo](https://www.tensorflow.org/api_docs/python/tf/data/experimental/bucket_by_sequence_length)).\n",
"\n",
"Drug pristop je uporaba **maskiranja**. V Kerasu nekatere plasti podpirajo dodatni vhod, ki kaže, katere žetone je treba upoštevati pri učenju. Da vključimo maskiranje v naš model, lahko dodamo ločeno plast `Masking` ([dokumentacija](https://keras.io/api/layers/core_layers/masking/)), ali pa določimo parameter `mask_zero=True` v naši plasti `Embedding`.\n",
"\n",
"> **Opomba**: To učenje bo trajalo približno 5 minut za dokončanje ene epohe na celotnem naboru podatkov. Če vam zmanjka potrpljenja, lahko učenje kadar koli prekinete. Prav tako lahko omejite količino podatkov, uporabljenih za učenje, tako da dodate klavzulo `.take(...)` po naborih podatkov `ds_train` in `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": [
"Zdaj, ko uporabljamo maskiranje, lahko model učimo na celotnem naboru naslovov in opisov.\n",
"\n",
"> **Opomba**: Ste opazili, da smo uporabljali vektorizator, ki je bil naučen na naslovih novic, in ne na celotnem besedilu članka? To lahko povzroči, da so nekateri tokeni prezrti, zato je bolje ponovno naučiti vektorizator. Vendar pa bo to verjetno imelo le zelo majhen učinek, zato bomo zaradi enostavnosti ostali pri prej naučenem vektorizatorju.\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## LSTM: Dolgoročni spomin\n",
"\n",
"Ena glavnih težav RNN-jev je **izginjanje gradientov**. RNN-ji so lahko precej dolgi in imajo lahko težave pri prenosu gradientov nazaj do prve plasti mreže med povratnim razširjanjem. Ko se to zgodi, mreža ne more učiti odnosov med oddaljenimi tokeni. Eden od načinov za izogibanje tej težavi je uvedba **eksplicitnega upravljanja stanja** z uporabo **vrat**. Dve najpogostejši arhitekturi, ki uvajata vrata, sta **dolgoročni spomin** (LSTM) in **enota z zapornimi vrati** (GRU). Tukaj bomo obravnavali LSTM-je.\n",
"\n",
"![Slika, ki prikazuje primer celice dolgoročnega spomina](../../../../../lessons/5-NLP/16-RNN/images/long-short-term-memory-cell.svg)\n",
"\n",
"LSTM mreža je organizirana podobno kot RNN, vendar se iz plasti v plast prenašata dve stanji: dejansko stanje $c$ in skriti vektor $h$. Pri vsaki enoti se skriti vektor $h_{t-1}$ združi z vhodom $x_t$, in skupaj nadzorujeta, kaj se zgodi s stanjem $c_t$ in izhodom $h_{t}$ prek **vrat**. Vsaka vrata imajo sigmoidno aktivacijo (izhod v območju $[0,1]$), ki jo lahko razumemo kot bitno masko, ko jo pomnožimo z vektorskim stanjem. LSTM-ji imajo naslednja vrata (od leve proti desni na zgornji sliki):\n",
"* **vrata za pozabo**, ki določajo, katere komponente vektorja $c_{t-1}$ moramo pozabiti in katere prenesti naprej.\n",
"* **vhodna vrata**, ki določajo, koliko informacij iz vhodnega vektorja in prejšnjega skritega vektorja je treba vključiti v vektorsko stanje.\n",
"* **izhodna vrata**, ki vzamejo novo vektorsko stanje in odločijo, katere njegove komponente bodo uporabljene za ustvarjanje novega skritega vektorja $h_t$.\n",
"\n",
"Komponente stanja $c$ lahko razumemo kot zastavice, ki jih lahko vklopimo ali izklopimo. Na primer, ko v zaporedju naletimo na ime *Alice*, domnevamo, da gre za žensko, in dvignemo zastavico v stanju, ki označuje, da imamo v stavku ženski samostalnik. Ko nato naletimo na besede *and Tom*, dvignemo zastavico, ki označuje, da imamo množinski samostalnik. Tako lahko z manipulacijo stanja sledimo slovničnim lastnostim stavka.\n",
"\n",
"> **Note**: Tukaj je odličen vir za razumevanje notranje strukture LSTM-jev: [Understanding LSTM Networks](https://colah.github.io/posts/2015-08-Understanding-LSTMs/) avtorja Christopherja Olaha.\n",
"\n",
"Čeprav se notranja struktura LSTM celice morda zdi zapletena, Keras to implementacijo skriva znotraj plasti `LSTM`, zato je edina stvar, ki jo moramo narediti v zgornjem primeru, zamenjava povratne plasti:\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": [
"## Dvosmerni in večplastni RNN-ji\n",
"\n",
"V naših dosedanjih primerih so rekurentne mreže delovale od začetka zaporedja do konca. To se nam zdi naravno, saj sledi isti smeri, kot beremo ali poslušamo govor. Vendar pa je za scenarije, ki zahtevajo naključni dostop do vhodnega zaporedja, bolj smiselno izvajati rekurentne izračune v obe smeri. RNN-ji, ki omogočajo izračune v obe smeri, se imenujejo **dvosmerni** RNN-ji, in jih lahko ustvarimo tako, da rekurentni sloj ovijemo s posebnim slojem `Bidirectional`.\n",
"\n",
"> **Note**: Sloj `Bidirectional` ustvari dve kopiji sloja znotraj njega in nastavi lastnost `go_backwards` ene od teh kopij na `True`, kar omogoča, da gre v nasprotno smer vzdolž zaporedja.\n",
"\n",
"Rekurentne mreže, enosmerne ali dvosmerne, zajamejo vzorce znotraj zaporedja in jih shranijo v vektorske stanja ali jih vrnejo kot izhod. Tako kot pri konvolucijskih mrežah lahko zgradimo še en rekurentni sloj, ki sledi prvemu, da zajame vzorce višje ravni, zgrajene iz vzorcev nižje ravni, ki jih je izvlekel prvi sloj. To nas pripelje do pojma **večplastnega RNN-ja**, ki je sestavljen iz dveh ali več rekurentnih mrež, kjer se izhod prejšnjega sloja posreduje naslednjemu sloju kot vhod.\n",
"\n",
"![Slika, ki prikazuje večplastni dolgoročno-kratkoročni pomnilniški RNN](../../../../../translated_images/multi-layer-lstm.dd975e29bb2a59fe58b429db833932d734c81f211cad2783797a9608984acb8c.sl.jpg)\n",
"\n",
"*Slika iz [tega odličnega prispevka](https://towardsdatascience.com/from-a-lstm-cell-to-a-multilayer-lstm-network-with-pytorch-2899eb5696f3) Fernanda Lópeza.*\n",
"\n",
"Keras omogoča enostavno sestavljanje teh mrež, saj morate modelu le dodati več rekurentnih slojev. Za vse sloje, razen zadnjega, moramo določiti parameter `return_sequences=True`, ker želimo, da sloj vrne vsa vmesna stanja, ne le končnega stanja rekurentnega izračuna.\n",
"\n",
"Zgradimo dvoslojni dvosmerni LSTM za naš klasifikacijski problem.\n",
"\n",
"> **Note** Ta koda ponovno zahteva precej časa za izvedbo, vendar nam daje najvišjo natančnost, ki smo jo do zdaj videli. Morda se torej splača počakati in preveriti rezultat.\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-ji za druge naloge\n",
"\n",
"Do zdaj smo se osredotočali na uporabo RNN-jev za razvrščanje zaporedij besedila. Vendar pa lahko obravnavajo še veliko več nalog, kot so generiranje besedila in strojno prevajanje — te naloge bomo obravnavali v naslednji enoti.\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"\n---\n\n**Omejitev odgovornosti**: \nTa dokument je bil preveden z uporabo storitve za strojno prevajanje [Co-op Translator](https://github.com/Azure/co-op-translator). Čeprav si prizadevamo za natančnost, vas prosimo, da upoštevate, da lahko avtomatizirani prevodi vsebujejo napake ali netočnosti. Izvirni dokument v njegovem izvirnem jeziku je treba obravnavati kot avtoritativni vir. Za ključne informacije priporočamo strokovno človeško prevajanje. Ne prevzemamo odgovornosti za morebitna nesporazuma ali napačne razlage, ki bi nastale zaradi uporabe tega prevoda.\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-30T08:09:56+00:00",
"source_file": "lessons/5-NLP/16-RNN/RNNTF.ipynb",
"language_code": "sl"
}
},
"nbformat": 4,
"nbformat_minor": 4
}

View File

@ -0,0 +1,414 @@
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Generativne mreže\n",
"\n",
"Rekurentne nevronske mreže (RNN) in njihove različice z vrati, kot so celice Long Short Term Memory (LSTM) in Gated Recurrent Units (GRU), omogočajo modeliranje jezika, tj. lahko se naučijo vrstnega reda besed in napovedujejo naslednjo besedo v zaporedju. To nam omogoča uporabo RNN za **generativne naloge**, kot so običajno generiranje besedila, strojno prevajanje in celo opisovanje slik.\n",
"\n",
"V arhitekturi RNN, ki smo jo obravnavali v prejšnji enoti, je vsaka enota RNN ustvarila naslednje skrito stanje kot izhod. Vendar pa lahko vsaki rekurentni enoti dodamo še en izhod, kar nam omogoča, da ustvarimo **zaporedje** (ki je enako dolžini izvirnega zaporedja). Poleg tega lahko uporabimo RNN enote, ki ne sprejemajo vhoda na vsakem koraku, temveč le začetni vektorski stanji, nato pa ustvarijo zaporedje izhodov.\n",
"\n",
"V tem zvezku se bomo osredotočili na preproste generativne modele, ki nam pomagajo ustvarjati besedilo. Za enostavnost bomo zgradili **mrežo na ravni znakov**, ki generira besedilo črko za črko. Med učenjem moramo vzeti neko besedilno zbirko in jo razdeliti na zaporedja črk.\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": [
"## Gradnja besedišča znakov\n",
"\n",
"Za gradnjo generativne mreže na ravni znakov moramo besedilo razdeliti na posamezne znake namesto besed. To lahko dosežemo z definiranjem drugačnega tokenizatorja:\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": [
"Poglejmo primer, kako lahko kodiramo besedilo iz našega nabora podatkov:\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": [
"## Učenje generativnega RNN\n",
"\n",
"Način, kako bomo učili RNN za generiranje besedila, je naslednji. Na vsakem koraku bomo vzeli zaporedje znakov dolžine `nchars` in mreži naročili, naj za vsak vhodni znak ustvari naslednji izhodni znak:\n",
"\n",
"![Slika, ki prikazuje primer generiranja besede 'HELLO' z RNN.](../../../../../translated_images/rnn-generate.56c54afb52f9781d63a7c16ea9c1b86cb70e6e1eae6a742b56b7b37468576b17.sl.png)\n",
"\n",
"Odvisno od dejanskega scenarija bomo morda želeli vključiti tudi nekatere posebne znake, kot je *konec zaporedja* `<eos>`. V našem primeru želimo mrežo naučiti generiranja neskončnega besedila, zato bomo določili, da je velikost vsakega zaporedja enaka `nchars` znakom. Posledično bo vsak učni primer sestavljen iz `nchars` vhodov in `nchars` izhodov (kar je vhodno zaporedje, premaknjeno za en simbol v levo). Miniserija bo sestavljena iz več takšnih zaporedij.\n",
"\n",
"Način, kako bomo generirali miniserije, je, da vzamemo vsak novičarski tekst dolžine `l` in iz njega ustvarimo vse možne kombinacije vhod-izhod (takšnih kombinacij bo `l-nchars`). Te bodo tvorile eno miniserijo, velikost miniserij pa bo pri vsakem učnem koraku različna.\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": [
"Zdaj bomo definirali generatorno mrežo. Lahko temelji na katerikoli ponavljajoči se celici, o katerih smo govorili v prejšnji enoti (preprosta, LSTM ali GRU). V našem primeru bomo uporabili LSTM.\n",
"\n",
"Ker mreža sprejema znake kot vhod, velikost besedišča pa je precej majhna, ne potrebujemo vgradne plasti; enovrstni kodiran vhod lahko neposredno preide v LSTM celico. Vendar pa, ker posredujemo številke znakov kot vhod, jih moramo pred posredovanjem v LSTM enovrstno kodirati. To se izvede z uporabo funkcije `one_hot` med prehodom `forward`. Izhodni kodirnik bo linearna plast, ki bo pretvorila skrito stanje v enovrstno kodiran izhod.\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": [
"Med usposabljanjem želimo imeti možnost vzorčenja generiranega besedila. Da bi to dosegli, bomo definirali funkcijo `generate`, ki bo ustvarila izhodni niz dolžine `size`, začenši z začetnim nizom `start`.\n",
"\n",
"Postopek deluje na naslednji način. Najprej bomo celoten začetni niz poslali skozi mrežo, kjer bomo dobili izhodno stanje `s` in naslednji napovedani znak `out`. Ker je `out` enovročno kodiran (one-hot encoded), uporabimo `argmax`, da dobimo indeks znaka `nc` v besedišču, nato pa uporabimo `itos`, da ugotovimo dejanski znak in ga dodamo v rezultatni seznam znakov `chars`. Ta postopek generiranja enega znaka ponovimo `size`-krat, da ustvarimo zahtevano število znakov.\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": [
"Zdaj pa začnimo z usposabljanjem! Zanka za usposabljanje je skoraj enaka kot v vseh naših prejšnjih primerih, vendar namesto natančnosti vsakih 1000 epohov izpišemo vzorčno generirano besedilo.\n",
"\n",
"Posebno pozornost je treba nameniti načinu izračuna izgube. Izgubo moramo izračunati glede na enovrstno kodiran izhod `out` in pričakovano besedilo `text_out`, ki je seznam indeksov znakov. Na srečo funkcija `cross_entropy` pričakuje nenormaliziran izhod mreže kot prvi argument in številko razreda kot drugi argument, kar je točno tisto, kar imamo. Prav tako samodejno izvaja povprečenje glede na velikost minibatcha.\n",
"\n",
"Usposabljanje omejimo tudi na `samples_to_train` vzorce, da ne čakamo predolgo. Spodbujamo vas, da eksperimentirate in poskusite daljše usposabljanje, morda za več epohov (v tem primeru bi morali ustvariti dodatno zanko okoli te kode).\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": [
"Ta primer že ustvarja precej dober tekst, vendar ga je mogoče izboljšati na več načinov:\n",
"\n",
"* **Boljša priprava minibatch-ov**. Način, kako smo pripravili podatke za učenje, je bil, da smo iz enega vzorca ustvarili en minibatch. To ni idealno, saj so minibatch-i različnih velikosti, nekateri pa sploh ne morejo biti ustvarjeni, ker je besedilo krajše od `nchars`. Poleg tega majhni minibatch-i ne obremenijo GPU dovolj učinkovito. Bolj smiselno bi bilo vzeti en velik kos besedila iz vseh vzorcev, nato ustvariti vse pare vhod-izhod, jih premešati in ustvariti minibatch-e enake velikosti.\n",
"\n",
"* **Večplastni LSTM**. Smiselno je poskusiti z 2 ali 3 plastmi LSTM celic. Kot smo omenili v prejšnji enoti, vsaka plast LSTM izlušči določene vzorce iz besedila, in pri generatorju na ravni znakov lahko pričakujemo, da bo nižja raven LSTM odgovorna za izluščanje zlogov, višje ravni pa za besede in besedne zveze. To je mogoče preprosto implementirati z dodajanjem parametra za število plasti v konstruktor LSTM.\n",
"\n",
"* Prav tako bi morda želeli eksperimentirati z **GRU enotami** in preveriti, katere delujejo bolje, ter z **različnimi velikostmi skritih plasti**. Prevelika skrita plast lahko privede do overfittinga (npr. mreža se bo naučila točno določenega besedila), premajhna velikost pa morda ne bo dala dobrih rezultatov.\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Mehko generiranje besedila in temperatura\n",
"\n",
"V prejšnji definiciji funkcije `generate` smo vedno izbrali znak z najvišjo verjetnostjo kot naslednji znak v generiranem besedilu. To je pogosto povzročilo, da se je besedilo \"ponavljalo\" med istimi zaporedji znakov znova in znova, kot v tem primeru:\n",
"```\n",
"today of the second the company and a second the company ...\n",
"```\n",
"\n",
"Če pa pogledamo porazdelitev verjetnosti za naslednji znak, lahko opazimo, da razlika med nekaj najvišjimi verjetnostmi ni velika, npr. en znak ima verjetnost 0,2, drugi pa 0,19 itd. Na primer, ko iščemo naslednji znak v zaporedju '*play*', je lahko naslednji znak prav tako presledek ali **e** (kot v besedi *player*).\n",
"\n",
"To nas pripelje do zaključka, da ni vedno \"pravično\" izbrati znaka z višjo verjetnostjo, saj lahko izbira drugega najverjetnejšega znaka še vedno vodi do smiselnega besedila. Bolj smiselno je **vzeti vzorec** znakov iz porazdelitve verjetnosti, ki jo poda izhod mreže.\n",
"\n",
"To vzorčenje lahko izvedemo z uporabo funkcije `multinomial`, ki implementira tako imenovano **multinomsko porazdelitev**. Funkcija, ki implementira to **mehko** generiranje besedila, je definirana spodaj:\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": [
"Predstavili smo še en parameter, imenovan **temperatura**, ki se uporablja za označevanje, kako strogo se moramo držati najvišje verjetnosti. Če je temperatura 1,0, izvajamo pošteno multinomialno vzorčenje, in ko temperatura gre proti neskončnosti - vse verjetnosti postanejo enake, naključno izberemo naslednji znak. V spodnjem primeru lahko opazimo, da besedilo postane nesmiselno, ko preveč povečamo temperaturo, in spominja na \"ciklirano\" težko generirano besedilo, ko se približa 0.\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"\n---\n\n**Omejitev odgovornosti**: \nTa dokument je bil preveden z uporabo storitve za strojno prevajanje [Co-op Translator](https://github.com/Azure/co-op-translator). Čeprav si prizadevamo za natančnost, vas prosimo, da upoštevate, da lahko avtomatizirani prevodi vsebujejo napake ali netočnosti. Izvirni dokument v njegovem izvirnem jeziku je treba obravnavati kot avtoritativni vir. Za ključne informacije priporočamo strokovni človeški prevod. Ne prevzemamo odgovornosti za morebitna nesporazumevanja ali napačne razlage, ki izhajajo iz uporabe tega prevoda.\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-30T08:00:59+00:00",
"source_file": "lessons/5-NLP/17-GenerativeNetworks/GenerativePyTorch.ipynb",
"language_code": "sl"
}
},
"nbformat": 4,
"nbformat_minor": 4
}

View File

@ -0,0 +1,497 @@
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Generativne mreže\n",
"\n",
"Rekurentne nevronske mreže (RNN) in njihove različice z vrati, kot so celice Long Short Term Memory (LSTM) in Gated Recurrent Units (GRU), omogočajo modeliranje jezika, tj. lahko se naučijo vrstnega reda besed in napovedujejo naslednjo besedo v zaporedju. To nam omogoča uporabo RNN za **generativne naloge**, kot so običajno generiranje besedila, strojno prevajanje in celo opisovanje slik.\n",
"\n",
"V arhitekturi RNN, ki smo jo obravnavali v prejšnji enoti, je vsaka enota RNN ustvarila naslednje skrito stanje kot izhod. Vendar pa lahko vsaki rekurentni enoti dodamo še en izhod, kar nam omogoča, da ustvarimo **zaporedje** (ki je enako dolžini izvirnega zaporedja). Poleg tega lahko uporabimo RNN enote, ki ne sprejemajo vhoda na vsakem koraku, ampak le začetni vektorski stanji, nato pa ustvarijo zaporedje izhodov.\n",
"\n",
"V tem zvezku se bomo osredotočili na preproste generativne modele, ki nam pomagajo ustvarjati besedilo. Za enostavnost bomo zgradili **mrežo na ravni znakov**, ki generira besedilo črko za črko. Med učenjem moramo vzeti nek korpus besedila in ga razdeliti na zaporedja črk.\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": [
"## Gradnja besedišča znakov\n",
"\n",
"Za izgradnjo generativne mreže na ravni znakov moramo besedilo razdeliti na posamezne znake namesto na besede. Plast `TextVectorization`, ki smo jo uporabljali prej, tega ne omogoča, zato imamo dve možnosti:\n",
"\n",
"* Ročno naložimo besedilo in izvedemo tokenizacijo \"na roke\", kot je prikazano v [tem uradnem primeru Keras](https://keras.io/examples/generative/lstm_character_level_text_generation/)\n",
"* Uporabimo razred `Tokenizer` za tokenizacijo na ravni znakov.\n",
"\n",
"Odločili se bomo za drugo možnost. `Tokenizer` se lahko uporablja tudi za tokenizacijo na ravni besed, zato je prehod z tokenizacije na ravni znakov na tokenizacijo na ravni besed precej preprost.\n",
"\n",
"Za izvedbo tokenizacije na ravni znakov moramo podati parameter `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": [
"Prav tako želimo uporabiti en poseben znak za označevanje **konca zaporedja**, ki ga bomo imenovali `<eos>`. Dodajmo ga ročno v besednjak:\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": [
"Zdaj lahko za kodiranje besedila v zaporedja števil uporabimo:\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": [
"## Učenje generativnega RNN za generiranje naslovov\n",
"\n",
"Način, kako bomo učili RNN za generiranje novičarskih naslovov, je naslednji. Na vsakem koraku bomo vzeli en naslov, ki bo podan v RNN, in za vsak vhodni znak bomo od mreže zahtevali, da generira naslednji izhodni znak:\n",
"\n",
"![Slika, ki prikazuje primer generiranja besede 'HELLO' z RNN.](../../../../../translated_images/rnn-generate.56c54afb52f9781d63a7c16ea9c1b86cb70e6e1eae6a742b56b7b37468576b17.sl.png)\n",
"\n",
"Za zadnji znak našega zaporedja bomo od mreže zahtevali, da generira `<eos>` token.\n",
"\n",
"Glavna razlika med generativnim RNN, ki ga uporabljamo tukaj, je v tem, da bomo vzeli izhod iz vsakega koraka RNN, ne le iz zadnje celice. To lahko dosežemo z nastavitvijo parametra `return_sequences` za RNN celico.\n",
"\n",
"Tako bo med učenjem vhod v mrežo zaporedje kodiranih znakov določene dolžine, izhod pa bo zaporedje iste dolžine, vendar premaknjeno za en element in zaključeno z `<eos>`. Minibatch bo sestavljen iz več takšnih zaporedij, pri čemer bomo morali uporabiti **polnjenje** (padding), da uskladimo vsa zaporedja.\n",
"\n",
"Ustvarimo funkcije, ki bodo za nas preoblikovale podatkovni niz. Ker želimo zaporedja polniti na ravni minibatch-a, bomo najprej razvrstili podatkovni niz z uporabo `.batch()`, nato pa ga `map`-ali, da izvedemo preoblikovanje. Tako bo funkcija za preoblikovanje kot parameter sprejela celoten minibatch:\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": [
"Nekaj pomembnih stvari, ki jih tukaj počnemo:\n",
"* Najprej izluščimo dejansko besedilo iz niza tensorja\n",
"* `text_to_sequences` pretvori seznam nizov v seznam celoštevilskih tensorjev\n",
"* `pad_sequences` dopolni te tensorje do njihove največje dolžine\n",
"* Na koncu vse znake kodiramo v enovrstično kodiranje, prav tako izvedemo premik in dodamo `<eos>`. Kmalu bomo videli, zakaj potrebujemo znake v enovrstičnem kodiranju.\n",
"\n",
"Vendar pa je ta funkcija **Pythonic**, kar pomeni, da je ni mogoče samodejno prevesti v Tensorflow računski graf. Če poskušamo to funkcijo neposredno uporabiti v funkciji `Dataset.map`, bomo dobili napake. To Pythonic klic moramo oviti z uporabo ovojnice `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": [
"> **Opomba**: Razlikovanje med Pythonovimi in Tensorflow funkcijami za transformacijo podatkov se morda zdi nekoliko zapleteno, in morda se sprašujete, zakaj ne transformiramo nabora podatkov z uporabo standardnih Pythonovih funkcij, preden ga posredujemo funkciji `fit`. Čeprav je to vsekakor mogoče, ima uporaba `Dataset.map` veliko prednost, saj se cevovod za transformacijo podatkov izvaja z uporabo Tensorflow računalniškega grafa, kar omogoča izkoriščanje GPU izračunov in zmanjšuje potrebo po prenašanju podatkov med CPU in GPU.\n",
"\n",
"Zdaj lahko zgradimo našo generativno mrežo in začnemo z učenjem. Temeljiti mora na katerikoli rekurentni celici, o kateri smo govorili v prejšnji enoti (enostavna, LSTM ali GRU). V našem primeru bomo uporabili LSTM.\n",
"\n",
"Ker mreža prejme znake kot vhod, velikost besedišča pa je precej majhna, ne potrebujemo vgraditvene plasti (embedding layer), saj lahko enovročno kodiran vhod neposredno vstopi v LSTM celico. Izhodna plast bo `Dense` klasifikator, ki bo pretvoril izhod LSTM v enovročno kodirane številke tokenov.\n",
"\n",
"Poleg tega, ker delamo z zaporedji spremenljive dolžine, lahko uporabimo plast `Masking`, da ustvarimo masko, ki bo ignorirala dopolnjene dele niza. To sicer ni strogo potrebno, saj nas ne zanima preveč vse, kar presega token `<eos>`, vendar jo bomo uporabili, da pridobimo nekaj izkušenj s to vrsto plasti. `input_shape` bo `(None, vocab_size)`, kjer `None` označuje zaporedje spremenljive dolžine, izhodna oblika pa je prav tako `(None, vocab_size)`, kot lahko vidite iz `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": [
"## Generiranje izhoda\n",
"\n",
"Zdaj, ko smo model izurili, ga želimo uporabiti za generiranje izhoda. Najprej potrebujemo način za dekodiranje besedila, predstavljenega kot zaporedje številk žetonov. Za to bi lahko uporabili funkcijo `tokenizer.sequences_to_texts`; vendar ta ne deluje dobro pri žetonizaciji na ravni znakov. Zato bomo vzeli slovar žetonov iz žetonizatorja (imenovan `word_index`), zgradili obratni zemljevid in napisali svojo funkcijo za dekodiranje:\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": [
"Zdaj bomo začeli z generacijo. Začeli bomo z neko začetno vrednostjo `start`, jo zakodirali v zaporedje `inp`, nato pa bomo v vsakem koraku poklicali našo mrežo, da izračuna naslednji znak.\n",
"\n",
"Izhod mreže `out` je vektor z `vocab_size` elementi, ki predstavljajo verjetnosti za vsak token. Najbolj verjetno številko tokena lahko najdemo z uporabo `argmax`. Ta znak nato dodamo v seznam generiranih tokenov in nadaljujemo z generacijo. Ta postopek generiranja enega znaka ponovimo `size`-krat, da ustvarimo želeno število znakov, pri čemer pa se postopek predčasno zaključi, če naletimo na `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": [
"## Vzorčenje izhoda med učenjem\n",
"\n",
"Ker nimamo nobenih uporabnih metrik, kot je *natančnost*, je edini način, da vidimo, ali se naš model izboljšuje, **vzorčenje** generiranih nizov med učenjem. Za to bomo uporabili **povratne klice**, tj. funkcije, ki jih lahko podamo funkciji `fit` in ki se bodo občasno klicale med učenjem.\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": [
"Ta primer že ustvarja precej dober tekst, vendar ga je mogoče izboljšati na več načinov:\n",
"\n",
"* **Več besedila**. Uporabili smo samo naslove za našo nalogo, vendar bi morda želeli eksperimentirati s celotnim besedilom. Ne pozabite, da RNN-ji niso najboljši pri obdelavi dolgih zaporedij, zato je smiselno, da jih razdelite na krajše stavke ali pa vedno trenirate na fiksni dolžini zaporedja z vnaprej določeno vrednostjo `num_chars` (recimo 256). Poskusite zgornji primer spremeniti v takšno arhitekturo, pri čemer uporabite [uradni Kerasov vodič](https://keras.io/examples/generative/lstm_character_level_text_generation/) kot navdih.\n",
"\n",
"* **Večplastni LSTM**. Smiselno je poskusiti z 2 ali 3 plastmi LSTM celic. Kot smo omenili v prejšnji enoti, vsaka plast LSTM izlušči določene vzorce iz besedila, in v primeru generatorja na ravni znakov lahko pričakujemo, da bo nižja raven LSTM odgovorna za izluščanje zlogov, višje ravni pa za besede in besedne zveze. To je mogoče preprosto implementirati z dodajanjem parametra za število plasti v konstruktor LSTM.\n",
"\n",
"* Prav tako bi morda želeli eksperimentirati z **GRU enotami** in preveriti, katere delujejo bolje, ter z **različnimi velikostmi skritih plasti**. Prevelika skrita plast lahko povzroči prekomerno prileganje (npr. mreža se bo naučila točno določenega besedila), premajhna velikost pa morda ne bo dala dobrih rezultatov.\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Generiranje mehkejšega besedila in temperatura\n",
"\n",
"V prejšnji definiciji funkcije `generate` smo vedno izbrali znak z najvišjo verjetnostjo kot naslednji znak v generiranem besedilu. To je pogosto povzročilo, da se je besedilo \"vrtelo\" med istimi zaporedji znakov znova in znova, kot v tem primeru:\n",
"```\n",
"today of the second the company and a second the company ...\n",
"```\n",
"\n",
"Če pa pogledamo porazdelitev verjetnosti za naslednji znak, lahko opazimo, da razlika med nekaj najvišjimi verjetnostmi ni velika, npr. en znak ima verjetnost 0.2, drugi pa 0.19 itd. Na primer, ko iščemo naslednji znak v zaporedju '*play*', je naslednji znak lahko enako verjetno presledek ali **e** (kot v besedi *player*).\n",
"\n",
"To nas pripelje do zaključka, da ni vedno \"pravično\" izbrati znak z višjo verjetnostjo, saj lahko izbira drugega najverjetnejšega znaka še vedno vodi do smiselnega besedila. Bolj smiselno je **vzorec** znakov vzeti iz porazdelitve verjetnosti, ki jo poda izhod mreže.\n",
"\n",
"To vzorčenje lahko izvedemo z uporabo funkcije `np.multinomial`, ki implementira tako imenovano **multinomsko porazdelitev**. Funkcija, ki implementira to **mehko** generiranje besedila, je definirana spodaj:\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": [
"Predstavili smo še en parameter, imenovan **temperatura**, ki se uporablja za označevanje, kako strogo se moramo držati najvišje verjetnosti. Če je temperatura 1,0, izvajamo pošteno multinomno vzorčenje, in ko temperatura gre proti neskončnosti - vse verjetnosti postanejo enake, naključno izberemo naslednji znak. V spodnjem primeru lahko opazimo, da besedilo postane nesmiselno, ko preveč povečamo temperaturo, in spominja na \"ciklirano\" težko generirano besedilo, ko se približa 0.\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"\n---\n\n**Omejitev odgovornosti**: \nTa dokument je bil preveden z uporabo storitve za prevajanje z umetno inteligenco [Co-op Translator](https://github.com/Azure/co-op-translator). Čeprav si prizadevamo za natančnost, vas prosimo, da upoštevate, da lahko avtomatizirani prevodi vsebujejo napake ali netočnosti. Izvirni dokument v njegovem izvirnem jeziku je treba obravnavati kot avtoritativni vir. Za ključne informacije priporočamo profesionalni človeški prevod. Ne prevzemamo odgovornosti za morebitna nesporazumevanja ali napačne razlage, ki bi nastale zaradi uporabe tega prevoda.\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-30T07:59:38+00:00",
"source_file": "lessons/5-NLP/17-GenerativeNetworks/GenerativeTF.ipynb",
"language_code": "sl"
}
},
"nbformat": 4,
"nbformat_minor": 4
}

View File

@ -0,0 +1,353 @@
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Mehanizmi pozornosti in transformatorji\n",
"\n",
"Ena glavnih pomanjkljivosti rekurentnih mrež je, da imajo vse besede v zaporedju enak vpliv na rezultat. To povzroča suboptimalno delovanje pri standardnih LSTM modelih kodirnik-dekodirnik za naloge, kot sta prepoznavanje imenovanih entitet in strojno prevajanje. V resnici imajo določene besede v vhodnem zaporedju pogosto večji vpliv na izhodne rezultate kot druge.\n",
"\n",
"Razmislimo o modelu zaporedje-v-zaporedje, kot je strojno prevajanje. Ta model je implementiran z dvema rekurentnima mrežama, kjer ena mreža (**kodirnik**) stisne vhodno zaporedje v skrito stanje, druga mreža (**dekodirnik**) pa to skrito stanje razširi v preveden rezultat. Težava pri tem pristopu je, da ima končno stanje mreže težave z zapomnitvijo začetka stavka, kar povzroča slabo kakovost modela pri dolgih stavkih.\n",
"\n",
"**Mehanizmi pozornosti** omogočajo tehtanje kontekstualnega vpliva vsakega vhodnega vektorja na vsako izhodno napoved RNN. To se implementira z ustvarjanjem bližnjic med vmesnimi stanji vhodne RNN in izhodne RNN. Na ta način pri generiranju izhodnega simbola $y_t$ upoštevamo vsa vhodna skrita stanja $h_i$ z različnimi utežnimi koeficienti $\\alpha_{t,i}$.\n",
"\n",
"![Slika, ki prikazuje model kodirnik/dekodirnik z aditivno plastjo pozornosti](../../../../../translated_images/encoder-decoder-attention.7a726296894fb567aa2898c94b17b3289087f6705c11907df8301df9e5eeb3de.sl.png)\n",
"*Model kodirnik-dekodirnik z mehanizmom aditivne pozornosti v [Bahdanau et al., 2015](https://arxiv.org/pdf/1409.0473.pdf), povzeto iz [tega bloga](https://lilianweng.github.io/lil-log/2018/06/24/attention-attention.html)*\n",
"\n",
"Matrika pozornosti $\\{\\alpha_{i,j}\\}$ predstavlja stopnjo, do katere določene vhodne besede vplivajo na generacijo določene besede v izhodnem zaporedju. Spodaj je primer takšne matrike:\n",
"\n",
"![Slika, ki prikazuje vzorčno poravnavo, najdeno z RNNsearch-50, povzeto iz Bahdanau - arviz.org](../../../../../translated_images/bahdanau-fig3.09ba2d37f202a6af11de6c82d2d197830ba5f4528d9ea430eb65fd3a75065973.sl.png)\n",
"\n",
"*Slika povzeta iz [Bahdanau et al., 2015](https://arxiv.org/pdf/1409.0473.pdf) (Slika 3)*\n",
"\n",
"Mehanizmi pozornosti so odgovorni za velik del trenutnega ali skoraj trenutnega stanja umetnosti na področju obdelave naravnega jezika. Dodajanje pozornosti pa močno poveča število parametrov modela, kar je povzročilo težave pri skaliranju RNN. Ključna omejitev pri skaliranju RNN je, da rekurentna narava modelov otežuje združevanje in paralelizacijo učenja. V RNN je treba vsak element zaporedja obdelati v zaporednem vrstnem redu, kar pomeni, da ga ni mogoče enostavno paralelizirati.\n",
"\n",
"Sprejetje mehanizmov pozornosti v kombinaciji s to omejitvijo je privedlo do nastanka zdajšnjih transformatorjev, ki predstavljajo stanje umetnosti, kot so BERT, OpenGPT3 in drugi.\n",
"\n",
"## Transformatorji\n",
"\n",
"Namesto da bi kontekst vsake prejšnje napovedi posredovali v naslednji korak ocenjevanja, **transformatorji** uporabljajo **pozicijske kodiranja** in pozornost za zajemanje konteksta določenega vhoda znotraj določenega okna besedila. Spodnja slika prikazuje, kako pozicijska kodiranja s pozornostjo zajamejo kontekst znotraj določenega okna.\n",
"\n",
"![Animiran GIF, ki prikazuje, kako se izvajajo ocene v transformatorjih.](../../../../../lessons/5-NLP/18-Transformers/images/transformer-animated-explanation.gif)\n",
"\n",
"Ker je vsak vhodni položaj neodvisno preslikan na vsak izhodni položaj, lahko transformatorji bolje paralelizirajo kot RNN, kar omogoča veliko večje in izrazitejše jezikovne modele. Vsaka glava pozornosti se lahko uporablja za učenje različnih odnosov med besedami, kar izboljša naloge obdelave naravnega jezika.\n",
"\n",
"**BERT** (Bidirectional Encoder Representations from Transformers) je zelo velik večplastni transformator z 12 plastmi za *BERT-base* in 24 za *BERT-large*. Model je najprej predhodno usposobljen na velikem korpusu besedilnih podatkov (Wikipedia + knjige) z uporabo nenadzorovanega učenja (napovedovanje zamaskiranih besed v stavku). Med predhodnim učenjem model pridobi pomembno raven razumevanja jezika, ki jo je nato mogoče uporabiti z drugimi nabori podatkov z uporabo prilagoditve. Ta proces se imenuje **prenosno učenje**.\n",
"\n",
"![Slika s http://jalammar.github.io/illustrated-bert/](../../../../../translated_images/jalammarBERT-language-modeling-masked-lm.34f113ea5fec4362e39ee4381aab7cad06b5465a0b5f053a0f2aa05fbe14e746.sl.png)\n",
"\n",
"Obstaja veliko različic arhitektur transformatorjev, vključno z BERT, DistilBERT, BigBird, OpenGPT3 in drugimi, ki jih je mogoče prilagoditi. Paket [HuggingFace](https://github.com/huggingface/) ponuja repozitorij za učenje mnogih teh arhitektur s PyTorch.\n",
"\n",
"## Uporaba BERT za klasifikacijo besedila\n",
"\n",
"Poglejmo, kako lahko uporabimo predhodno usposobljen model BERT za reševanje naše tradicionalne naloge: klasifikacije zaporedij. Klasificirali bomo naš izvirni nabor podatkov AG News.\n",
"\n",
"Najprej naložimo knjižnico HuggingFace in naš nabor podatkov:\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": [
"Ker bomo uporabljali vnaprej naučen BERT model, bomo morali uporabiti določen tokenizer. Najprej bomo naložili tokenizer, povezan z vnaprej naučenim BERT modelom.\n",
"\n",
"Knjižnica HuggingFace vsebuje repozitorij vnaprej naučenih modelov, ki jih lahko uporabite tako, da njihove imena podate kot argumente funkcijam `from_pretrained`. Vsi potrebni binarni datoteki za model bodo samodejno preneseni.\n",
"\n",
"V določenih primerih pa boste morali naložiti svoje modele. V tem primeru lahko določite imenik, ki vsebuje vse ustrezne datoteke, vključno s parametri za tokenizer, datoteko `config.json` s parametri modela, binarnimi utežmi itd.\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` objekt vsebuje funkcijo `encode`, ki jo je mogoče neposredno uporabiti za kodiranje besedila:\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": [
"Potem ustvarimo iteratorje, ki jih bomo uporabljali med treningom za dostop do podatkov. Ker BERT uporablja svojo lastno funkcijo kodiranja, bi morali definirati funkcijo za dodajanje presledkov, podobno kot `padify`, ki smo jo definirali prej:\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": [
"V našem primeru bomo uporabili vnaprej naučen BERT model, imenovan `bert-base-uncased`. Naložimo model z uporabo paketa `BertForSequenceClassification`. To zagotavlja, da ima naš model že zahtevano arhitekturo za klasifikacijo, vključno s končnim klasifikatorjem. Prikazalo se bo opozorilno sporočilo, ki navaja, da uteži končnega klasifikatorja niso inicializirane in da model potrebuje predhodno učenje - to je povsem v redu, saj je točno to tisto, kar bomo storili!\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": [
"Zdaj smo pripravljeni na začetek učenja! Ker je BERT že predhodno naučen, želimo začeti z razmeroma majhno hitrostjo učenja, da ne bi uničili začetnih uteži.\n",
"\n",
"Vso zahtevno delo opravi model `BertForSequenceClassification`. Ko model pokličemo na učnih podatkih, vrne tako izgubo kot izhod omrežja za vhodni minibatch. Izgubo uporabimo za optimizacijo parametrov (`loss.backward()` izvede povratno propagacijo), medtem ko `out` uporabimo za izračun učne natančnosti, tako da primerjamo dobljene oznake `labs` (izračunane z uporabo `argmax`) s pričakovanimi `labels`.\n",
"\n",
"Za nadzor procesa akumuliramo izgubo in natančnost skozi več iteracij ter ju izpišemo vsakih `report_freq` učnih ciklov.\n",
"\n",
"To učenje bo verjetno trajalo kar nekaj časa, zato omejimo število iteracij.\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": [
"Vidite lahko (še posebej, če povečate število iteracij in počakate dovolj dolgo), da nam klasifikacija z BERT daje precej dobro natančnost! To je zato, ker BERT že zelo dobro razume strukturo jezika, mi pa moramo le prilagoditi končni klasifikator. Vendar pa, ker je BERT velik model, celoten proces učenja traja dolgo in zahteva veliko računske moči! (GPU, in po možnosti več kot enega).\n",
"\n",
"> **Opomba:** V našem primeru uporabljamo enega najmanjših vnaprej naučenih BERT modelov. Obstajajo večji modeli, ki bodo verjetno dali boljše rezultate.\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Ocena zmogljivosti modela\n",
"\n",
"Zdaj lahko ocenimo zmogljivost našega modela na testnem naboru podatkov. Zanka za ocenjevanje je precej podobna zanki za učenje, vendar ne smemo pozabiti preklopiti modela v način ocenjevanja z uporabo `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": [
"## Ključne točke\n",
"\n",
"V tej enoti smo videli, kako enostavno je vzeti vnaprej naučen jezikovni model iz knjižnice **transformers** in ga prilagoditi naši nalogi razvrščanja besedil. Podobno lahko modele BERT uporabimo za ekstrakcijo entitet, odgovarjanje na vprašanja in druge naloge obdelave naravnega jezika.\n",
"\n",
"Transformers modeli predstavljajo trenutno najsodobnejši pristop v obdelavi naravnega jezika (NLP), in v večini primerov bi morali biti prva rešitev, s katero začnete eksperimentirati pri implementaciji prilagojenih NLP rešitev. Vendar pa je razumevanje osnovnih principov rekurentnih nevronskih mrež, o katerih smo govorili v tem modulu, izjemno pomembno, če želite graditi napredne nevronske modele.\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"\n---\n\n**Omejitev odgovornosti**: \nTa dokument je bil preveden z uporabo storitve za strojno prevajanje [Co-op Translator](https://github.com/Azure/co-op-translator). Čeprav si prizadevamo za natančnost, vas prosimo, da se zavedate, da lahko avtomatizirani prevodi vsebujejo napake ali netočnosti. Izvirni dokument v njegovem izvirnem jeziku je treba obravnavati kot avtoritativni vir. Za ključne informacije priporočamo strokovno človeško prevajanje. Ne prevzemamo odgovornosti za morebitna nesporazumevanja ali napačne razlage, ki izhajajo iz uporabe tega prevoda.\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-30T08:04:27+00:00",
"source_file": "lessons/5-NLP/18-Transformers/TransformersPyTorch.ipynb",
"language_code": "sl"
}
},
"nbformat": 4,
"nbformat_minor": 4
}

View File

@ -0,0 +1,819 @@
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Mehanizmi pozornosti in transformatorji\n",
"\n",
"Ena glavnih pomanjkljivosti rekurzivnih omrežij je, da imajo vse besede v zaporedju enak vpliv na rezultat. To povzroča suboptimalno delovanje pri standardnih modelih LSTM kodirnik-dekodirnik za naloge zaporedja v zaporedje, kot so prepoznavanje imenovanih entitet in strojno prevajanje. V resnici imajo določene besede v vhodnem zaporedju pogosto večji vpliv na izhodne rezultate kot druge.\n",
"\n",
"Razmislimo o modelu zaporedja v zaporedje, kot je strojno prevajanje. Ta model je implementiran z dvema rekurzivnima omrežjema, kjer eno omrežje (**kodirnik**) stisne vhodno zaporedje v skrito stanje, drugo omrežje (**dekodirnik**) pa razširi to skrito stanje v preveden rezultat. Težava pri tem pristopu je, da ima končno stanje omrežja težave pri pomnjenju začetka stavka, kar povzroča slabšo kakovost modela pri dolgih stavkih.\n",
"\n",
"**Mehanizmi pozornosti** omogočajo tehtanje kontekstualnega vpliva vsakega vhodnega vektorja na vsako izhodno napoved RNN. To se implementira z ustvarjanjem bližnjic med vmesnimi stanji vhodnega RNN in izhodnega RNN. Na ta način, ko generiramo izhodni simbol $y_t$, upoštevamo vsa skrita stanja vhodov $h_i$ z različnimi utežnimi koeficienti $\\alpha_{t,i}$.\n",
"\n",
"![Slika prikazuje model kodirnik/dekodirnik z dodatno plastjo pozornosti](../../../../../translated_images/encoder-decoder-attention.7a726296894fb567aa2898c94b17b3289087f6705c11907df8301df9e5eeb3de.sl.png)\n",
"*Model kodirnik-dekodirnik z mehanizmom dodatne pozornosti v [Bahdanau et al., 2015](https://arxiv.org/pdf/1409.0473.pdf), citirano iz [tega bloga](https://lilianweng.github.io/lil-log/2018/06/24/attention-attention.html)*\n",
"\n",
"Matrika pozornosti $\\{\\alpha_{i,j}\\}$ predstavlja stopnjo, do katere določene vhodne besede vplivajo na generacijo določene besede v izhodnem zaporedju. Spodaj je primer takšne matrike:\n",
"\n",
"![Slika prikazuje vzorčno poravnavo, ki jo je našel RNNsearch-50, vzeto iz Bahdanau - arviz.org](../../../../../translated_images/bahdanau-fig3.09ba2d37f202a6af11de6c82d2d197830ba5f4528d9ea430eb65fd3a75065973.sl.png)\n",
"\n",
"*Slika vzeta iz [Bahdanau et al., 2015](https://arxiv.org/pdf/1409.0473.pdf) (Fig.3)*\n",
"\n",
"Mehanizmi pozornosti so odgovorni za velik del trenutnega ali skoraj trenutnega stanja umetnosti na področju obdelave naravnega jezika. Dodajanje pozornosti pa močno poveča število parametrov modela, kar je povzročilo težave pri skaliranju RNN-jev. Ključna omejitev skaliranja RNN-jev je, da rekurzivna narava modelov otežuje združevanje in paralelizacijo učenja. Pri RNN mora biti vsak element zaporedja obdelan v zaporednem vrstnem redu, kar pomeni, da ga ni mogoče enostavno paralelizirati.\n",
"\n",
"Uporaba mehanizmov pozornosti v kombinaciji s to omejitvijo je privedla do nastanka transformatorjev, ki so trenutno stanje umetnosti in jih danes poznamo ter uporabljamo, od BERT do OpenGPT3.\n",
"\n",
"## Transformatorji\n",
"\n",
"Namesto da bi prenašali kontekst vsake prejšnje napovedi v naslednji korak ocenjevanja, **transformatorji** uporabljajo **pozicijske kodiranja** in **pozornost**, da zajamejo kontekst danega vhoda znotraj določenega okna besedila. Spodnja slika prikazuje, kako pozicijska kodiranja s pozornostjo zajamejo kontekst znotraj določenega okna.\n",
"\n",
"![Animiran GIF prikazuje, kako se ocene izvajajo v transformatorjih.](../../../../../lessons/5-NLP/18-Transformers/images/transformer-animated-explanation.gif)\n",
"\n",
"Ker je vsaka vhodna pozicija neodvisno preslikana v vsako izhodno pozicijo, transformatorji omogočajo boljšo paralelizacijo kot RNN-ji, kar omogoča veliko večje in bolj izrazne jezikovne modele. Vsaka glava pozornosti se lahko uporablja za učenje različnih odnosov med besedami, kar izboljša naloge obdelave naravnega jezika.\n",
"\n",
"## Gradnja preprostega modela transformatorja\n",
"\n",
"Keras ne vsebuje vgrajene plasti transformatorja, vendar jo lahko zgradimo sami. Kot prej se bomo osredotočili na klasifikacijo besedila iz nabora podatkov AG News, vendar je vredno omeniti, da transformatorji kažejo najboljše rezultate pri težjih nalogah obdelave naravnega jezika.\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": [
"Nove plasti v Kerasu morajo podrazrediti razred `Layer` in implementirati metodo `call`. Začnimo s plastjo **Positional Embedding**. Uporabili bomo [nekaj kode iz uradne dokumentacije Keras](https://keras.io/examples/nlp/text_classification_with_transformer/). Predpostavljali bomo, da zapolnimo vse vhodne zaporedja na dolžino `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": [
"Ta plast je sestavljena iz dveh `Embedding` plasti: za vdelavo tokenov (na način, ki smo ga že obravnavali) in položajev tokenov. Položaji tokenov so ustvarjeni kot zaporedje naravnih števil od 0 do `maxlen` z uporabo `tf.range`, nato pa so posredovani skozi vdelano plast. Dva nastala vdelana vektorja se nato seštejeta, kar ustvari položajsko vdelano predstavitev vhodnih podatkov oblike `maxlen`$\\times$`embed_dim`.\n",
"\n",
"Zdaj pa implementirajmo transformacijski blok. Ta bo sprejel izhod prej definirane vdelane plasti:\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": [
"Zdaj smo pripravljeni definirati celoten model transformatorja:\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 modeli\n",
"\n",
"**BERT** (Bidirectional Encoder Representations from Transformers) je zelo velik večplastni transformatorni model z 12 plastmi za *BERT-base* in 24 za *BERT-large*. Model je najprej predhodno usposobljen na velikem korpusu besedilnih podatkov (WikiPedia + knjige) z uporabo nenadzorovanega učenja (napovedovanje zakritih besed v stavku). Med predhodnim usposabljanjem model pridobi pomembno raven razumevanja jezika, ki jo je nato mogoče uporabiti z drugimi nabori podatkov prek finega prilagajanja. Ta proces se imenuje **prenosno učenje**.\n",
"\n",
"![slika s http://jalammar.github.io/illustrated-bert/](../../../../../translated_images/jalammarBERT-language-modeling-masked-lm.34f113ea5fec4362e39ee4381aab7cad06b5465a0b5f053a0f2aa05fbe14e746.sl.png)\n",
"\n",
"Obstaja veliko različic transformatornih arhitektur, vključno z BERT, DistilBERT, BigBird, OpenGPT3 in drugimi, ki jih je mogoče fino prilagoditi.\n",
"\n",
"Poglejmo, kako lahko uporabimo predhodno usposobljen model BERT za reševanje našega tradicionalnega problema klasifikacije zaporedij. Idejo in nekaj kode bomo vzeli iz [uradne dokumentacije](https://www.tensorflow.org/text/tutorials/classify_text_with_bert).\n",
"\n",
"Za nalaganje predhodno usposobljenih modelov bomo uporabili **Tensorflow hub**. Najprej naložimo BERT-specifični vektorizator:\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": [
"Pomembno je, da uporabite isti vektorizator, kot je bil uporabljen pri treniranju originalnega omrežja. Poleg tega BERT vektorizator vrne tri komponente:\n",
"* `input_word_ids`, ki je zaporedje številk tokenov za vhodni stavek\n",
"* `input_mask`, ki prikazuje, kateri del zaporedja vsebuje dejanski vhod in kateri del je polnilo. To je podobno maski, ki jo ustvari plast `Masking`\n",
"* `input_type_ids` se uporablja za naloge jezikovnega modeliranja in omogoča določitev dveh vhodnih stavkov v enem zaporedju.\n",
"\n",
"Nato lahko ustvarimo BERT ekstraktor značilnosti:\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": [
"Torej, BERT plast vrne več uporabnih rezultatov:\n",
"* `pooled_output` je rezultat povprečenja vseh tokenov v zaporedju. Lahko ga razumete kot inteligentno semantično vdelavo celotnega omrežja. To je enakovredno izhodu sloja `GlobalAveragePooling1D` v našem prejšnjem modelu.\n",
"* `sequence_output` je izhod zadnje transformer plasti (ustreza izhodu `TransformerBlock` v našem zgornjem modelu).\n",
"* `encoder_outputs` so izhodi vseh transformer plasti. Ker smo naložili 4-slojni BERT model (kot verjetno lahko sklepate iz imena, ki vsebuje `4_H`), ima 4 tenzorje. Zadnji je enak kot `sequence_output`.\n",
"\n",
"Zdaj bomo definirali končni model za klasifikacijo. Uporabili bomo *funkcionalno definicijo modela*, kjer definiramo vhod modela in nato podamo niz izrazov za izračun njegovega izhoda. Prav tako bomo uteži BERT modela nastavili kot netrenirljive in trenirali samo končni klasifikator:\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": [
"Kljub temu, da je malo parametrov za učenje, je proces precej počasen, saj je BERT-ov ekstraktor značilnosti računsko zahteven. Zdi se, da nismo uspeli doseči zadovoljive natančnosti, bodisi zaradi pomanjkanja učenja ali pomanjkanja parametrov modela.\n",
"\n",
"Poskusimo odmrzniti uteži BERT-a in ga tudi trenirati. To zahteva zelo majhno hitrost učenja ter bolj previdno strategijo učenja z **ogrevanjem** in uporabo optimizatorja **AdamW**. Za ustvarjanje optimizatorja bomo uporabili paket `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": [
"Kot lahko vidiš, gre učenje precej počasi vendar bi morda želel eksperimentirati in model trenirati za nekaj epochov (510) ter preveriti, ali lahko dosežeš boljši rezultat v primerjavi s pristopi, ki smo jih uporabili prej.\n",
"\n",
"## Knjižnica Huggingface Transformers\n",
"\n",
"Drug zelo pogost (in nekoliko enostavnejši) način uporabe Transformer modelov je [HuggingFace paket](https://github.com/huggingface/), ki ponuja preproste gradnike za različne naloge obdelave naravnega jezika (NLP). Na voljo je tako za Tensorflow kot za PyTorch, še en zelo priljubljen okvir za nevronske mreže.\n",
"\n",
"> **Opomba**: Če te ne zanima, kako deluje knjižnica Transformers, lahko preskočiš na konec tega zvezka, saj ne boš videl ničesar bistveno drugačnega od tega, kar smo naredili zgoraj. Ponovili bomo iste korake treniranja BERT modela z uporabo druge knjižnice in bistveno večjega modela. Zato proces vključuje precej dolgotrajno učenje, tako da morda želiš le preleteti kodo.\n",
"\n",
"Poglejmo, kako lahko naš problem rešimo z uporabo [Huggingface Transformers](http://huggingface.co).\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Najprej moramo izbrati model, ki ga bomo uporabljali. Poleg nekaterih vgrajenih modelov Huggingface vsebuje [spletni repozitorij modelov](https://huggingface.co/models), kjer lahko najdete veliko več predhodno naučenih modelov, ki jih je prispevala skupnost. Vse te modele je mogoče naložiti in uporabljati zgolj z navedbo imena modela. Vsi potrebni binarni datoteki za model se bodo samodejno prenesli.\n",
"\n",
"V določenih primerih boste morali naložiti svoje modele, v tem primeru lahko določite imenik, ki vsebuje vse ustrezne datoteke, vključno s parametri za tokenizer, datoteko `config.json` s parametri modela, binarne uteži itd.\n",
"\n",
"Iz imena modela lahko ustvarimo tako model kot tokenizer. Začnimo s tokenizerjem:\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` objekt vsebuje funkcijo `encode`, ki jo je mogoče neposredno uporabiti za kodiranje besedila:\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": [
"Prav tako lahko uporabimo tokenizer za kodiranje zaporedja na način, ki je primeren za posredovanje modelu, tj. vključno s polji `token_ids`, `input_mask` itd. Prav tako lahko določimo, da želimo Tensorflow tenzorje, tako da podamo argument `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": [
"V našem primeru bomo uporabili vnaprej naučen BERT model z imenom `bert-base-uncased`. *Uncased* pomeni, da je model neobčutljiv na velike in male črke.\n",
"\n",
"Pri treniranju modela moramo kot vhod zagotoviti tokenizirano zaporedje, zato bomo zasnovali podatkovno procesno cevovod. Ker je `tokenizer.encode` Python funkcija, bomo uporabili enak pristop kot v prejšnji enoti, kjer jo pokličemo z uporabo `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": [
"Zdaj lahko naložimo dejanski model z uporabo paketa `BertForSequenceClassification`. To zagotavlja, da ima naš model že zahtevano arhitekturo za klasifikacijo, vključno s končnim klasifikatorjem. Videli boste opozorilno sporočilo, ki navaja, da uteži končnega klasifikatorja niso inicializirane in da model potrebuje predhodno učenje - to je povsem v redu, saj je točno to tisto, kar bomo storili!\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": [
"Kot lahko vidite iz `summary()`, model vsebuje skoraj 110 milijonov parametrov! Predvidoma, če želimo preprosto nalogo klasifikacije na razmeroma majhnem naboru podatkov, ne želimo trenirati osnovnega sloja 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": [
"Zdaj smo pripravljeni na začetek usposabljanja!\n",
"\n",
"> **Opomba**: Usposabljanje celotnega modela BERT lahko vzame veliko časa! Zato ga bomo usposabljali le za prvih 32 serij. To je zgolj za prikaz, kako je usposabljanje modela nastavljeno. Če vas zanima polno usposabljanje - preprosto odstranite parametra `steps_per_epoch` in `validation_steps` ter se pripravite na čakanje!\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": [
"Če povečate število iteracij in počakate dovolj dolgo ter trenirate skozi več epochov, lahko pričakujete, da bo klasifikacija z BERT dosegla najboljšo natančnost! To je zato, ker BERT že zelo dobro razume strukturo jezika, zato je potrebno le prilagoditi končni klasifikator. Vendar pa, ker je BERT velik model, celoten proces učenja traja dolgo in zahteva veliko računske moči! (GPU, in po možnosti več kot enega).\n",
"\n",
"> **Opomba:** V našem primeru uporabljamo enega najmanjših vnaprej naučenih modelov BERT. Obstajajo večji modeli, ki bodo verjetno dali boljše rezultate.\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Ključne točke\n",
"\n",
"V tej enoti smo si ogledali zelo nedavne arhitekture modelov, ki temeljijo na **transformerjih**. Uporabili smo jih za našo nalogo klasifikacije besedila, vendar lahko modele BERT podobno uporabimo tudi za ekstrakcijo entitet, odgovarjanje na vprašanja in druge naloge obdelave naravnega jezika (NLP).\n",
"\n",
"Transformer modeli predstavljajo trenutno najsodobnejši pristop v NLP-ju in v večini primerov bi morali biti prva rešitev, s katero začnete eksperimentirati pri implementaciji prilagojenih NLP rešitev. Kljub temu je razumevanje osnovnih načel rekurentnih nevronskih mrež, o katerih smo govorili v tem modulu, izjemno pomembno, če želite graditi napredne nevronske modele.\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": []
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"\n---\n\n**Omejitev odgovornosti**: \nTa dokument je bil preveden z uporabo storitve za prevajanje z umetno inteligenco [Co-op Translator](https://github.com/Azure/co-op-translator). Čeprav si prizadevamo za natančnost, vas prosimo, da upoštevate, da lahko avtomatizirani prevodi vsebujejo napake ali netočnosti. Izvirni dokument v njegovem maternem jeziku je treba obravnavati kot avtoritativni vir. Za ključne informacije priporočamo profesionalni človeški prevod. Ne prevzemamo odgovornosti za morebitna nesporazume ali napačne razlage, ki bi nastale zaradi uporabe tega prevoda.\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-30T08:06:40+00:00",
"source_file": "lessons/5-NLP/18-Transformers/TransformersTF.ipynb",
"language_code": "sl"
}
},
"nbformat": 4,
"nbformat_minor": 4
}

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