chore(i18n): sync translations with latest source changes (chunk 5/8, 92 changes)
This commit is contained in:
parent
42824f786e
commit
d01d40ddb2
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
|
|
@ -0,0 +1,178 @@
|
|||
# 物件偵測
|
||||
|
||||
我們之前處理的影像分類模型是將一張圖片輸入,並產生一個分類結果,例如在 MNIST 問題中,分類結果是「數字」這個類別。然而,在許多情況下,我們不僅僅想知道圖片中有物件,我們還希望能夠確定它們的精確位置。這正是**物件偵測**的目的。
|
||||
|
||||
## [課前測驗](https://ff-quizzes.netlify.app/en/ai/quiz/21)
|
||||
|
||||

|
||||
|
||||
> 圖片來源:[YOLO v2 官方網站](https://pjreddie.com/darknet/yolov2/)
|
||||
|
||||
## 一個簡單的物件偵測方法
|
||||
|
||||
假設我們想在一張圖片中找到一隻貓,一個非常簡單的物件偵測方法如下:
|
||||
|
||||
1. 將圖片分割成多個小區塊。
|
||||
2. 對每個區塊進行影像分類。
|
||||
3. 將分類結果中激活值足夠高的區塊視為包含目標物件的區域。
|
||||
|
||||

|
||||
|
||||
> *圖片來源:[練習筆記本](ObjectDetection-TF.ipynb)*
|
||||
|
||||
然而,這種方法遠非理想,因為它只能非常粗略地定位物件的邊界框。若要更精確地定位,我們需要進行某種**回歸**來預測邊界框的座標,而這需要特定的數據集。
|
||||
|
||||
## 用回歸進行物件偵測
|
||||
|
||||
[這篇部落格文章](https://towardsdatascience.com/object-detection-with-neural-networks-a4e2c46b4491)對於偵測形狀提供了一個很好的入門介紹。
|
||||
|
||||
## 物件偵測的數據集
|
||||
|
||||
在進行物件偵測時,你可能會遇到以下數據集:
|
||||
|
||||
* [PASCAL VOC](http://host.robots.ox.ac.uk/pascal/VOC/) - 包含 20 個類別
|
||||
* [COCO](http://cocodataset.org/#home) - 常見物件上下文數據集,包含 80 個類別、邊界框和分割遮罩
|
||||
|
||||

|
||||
|
||||
## 物件偵測的評估指標
|
||||
|
||||
### 交集比聯集 (Intersection over Union, IoU)
|
||||
|
||||
對於影像分類來說,衡量算法表現的方式很簡單,但對於物件偵測,我們需要同時衡量類別的正確性以及推測邊界框位置的精確性。後者使用所謂的**交集比聯集** (IoU) 來衡量,這是一種用來測量兩個框(或任意兩個區域)重疊程度的方法。
|
||||
|
||||

|
||||
|
||||
> *圖片來源:[這篇優秀的 IoU 部落格文章](https://pyimagesearch.com/2016/11/07/intersection-over-union-iou-for-object-detection/)*
|
||||
|
||||
概念很簡單——我們將兩個圖形的交集面積除以它們的聯集面積。對於完全相同的區域,IoU 值為 1;對於完全不相交的區域,IoU 值為 0;其他情況下,IoU 值介於 0 到 1 之間。我們通常只考慮 IoU 超過某個值的邊界框。
|
||||
|
||||
### 平均準確率 (Average Precision, AP)
|
||||
|
||||
假設我們想衡量某個類別 $C$ 的物件被識別的效果。為此,我們使用**平均準確率** (AP) 指標,其計算方式如下:
|
||||
|
||||
1. 繪製準確率-召回率曲線,顯示準確率隨檢測閾值(從 0 到 1)的變化。
|
||||
2. 根據閾值,我們會在圖片中檢測到更多或更少的物件,並得到不同的準確率和召回率值。
|
||||
3. 曲線看起來如下:
|
||||
|
||||
<img src="https://github.com/shwars/NeuroWorkshop/raw/master/images/ObjDetectionPrecisionRecall.png"/>
|
||||
|
||||
> *圖片來源:[NeuroWorkshop](http://github.com/shwars/NeuroWorkshop)*
|
||||
|
||||
對於給定類別 $C$ 的平均準確率是該曲線下的面積。更精確地說,召回率軸通常被分成 10 個部分,並對這些點的準確率取平均值:
|
||||
|
||||
$$
|
||||
AP = {1\over11}\sum_{i=0}^{10}\mbox{Precision}(\mbox{Recall}={i\over10})
|
||||
$$
|
||||
|
||||
### AP 與 IoU
|
||||
|
||||
我們只考慮那些 IoU 超過某個值的檢測。例如,在 PASCAL VOC 數據集中,通常假設 $\mbox{IoU Threshold} = 0.5$,而在 COCO 數據集中,AP 是針對不同的 $\mbox{IoU Threshold}$ 值進行測量的。
|
||||
|
||||
<img src="https://github.com/shwars/NeuroWorkshop/raw/master/images/ObjDetectionPrecisionRecallIoU.png"/>
|
||||
|
||||
> *圖片來源:[NeuroWorkshop](http://github.com/shwars/NeuroWorkshop)*
|
||||
|
||||
### 平均平均準確率 (Mean Average Precision, mAP)
|
||||
|
||||
物件偵測的主要評估指標稱為**平均平均準確率** (Mean Average Precision, mAP)。它是所有物件類別的平均準確率的平均值,有時也包括不同 $\mbox{IoU Threshold}$ 的平均值。更詳細的 mAP 計算過程可以參考[這篇部落格文章](https://medium.com/@timothycarlen/understanding-the-map-evaluation-metric-for-object-detection-a07fe6962cf3)),以及[這裡的程式碼範例](https://gist.github.com/tarlen5/008809c3decf19313de216b9208f3734)。
|
||||
|
||||
## 不同的物件偵測方法
|
||||
|
||||
物件偵測算法大致分為兩類:
|
||||
|
||||
* **區域提議網絡** (Region Proposal Networks, R-CNN, Fast R-CNN, Faster R-CNN)。主要思想是生成**感興趣區域** (ROI),並對其運行 CNN,尋找最大激活值。這與簡單方法有些相似,但 ROI 是以更聰明的方式生成的。這類方法的一個主要缺點是速度較慢,因為需要對圖片進行多次 CNN 分類。
|
||||
* **單次通過** (One-pass) 方法(如 YOLO、SSD、RetinaNet)。這些架構設計成在一次通過中同時預測類別和 ROI。
|
||||
|
||||
### R-CNN: 基於區域的 CNN
|
||||
|
||||
[R-CNN](http://islab.ulsan.ac.kr/files/announcement/513/rcnn_pami.pdf) 使用 [Selective Search](http://www.huppelen.nl/publications/selectiveSearchDraft.pdf) 生成 ROI 區域的層次結構,然後通過 CNN 特徵提取器和 SVM 分類器來確定物件類別,並通過線性回歸確定*邊界框*座標。[官方論文](https://arxiv.org/pdf/1506.01497v1.pdf)
|
||||
|
||||

|
||||
|
||||
> *圖片來源:van de Sande et al. ICCV’11*
|
||||
|
||||

|
||||
|
||||
> *圖片來源:[這篇部落格](https://towardsdatascience.com/r-cnn-fast-r-cnn-faster-r-cnn-yolo-object-detection-algorithms-36d53571365e)*
|
||||
|
||||
### F-RCNN - 快速 R-CNN
|
||||
|
||||
這種方法與 R-CNN 類似,但區域是在卷積層應用後定義的。
|
||||
|
||||

|
||||
|
||||
> 圖片來源:[官方論文](https://www.cv-foundation.org/openaccess/content_iccv_2015/papers/Girshick_Fast_R-CNN_ICCV_2015_paper.pdf),[arXiv](https://arxiv.org/pdf/1504.08083.pdf),2015
|
||||
|
||||
### Faster R-CNN
|
||||
|
||||
這種方法的主要思想是使用神經網絡來預測 ROI,即所謂的*區域提議網絡* (Region Proposal Network)。[論文](https://arxiv.org/pdf/1506.01497.pdf),2016
|
||||
|
||||

|
||||
|
||||
> 圖片來源:[官方論文](https://arxiv.org/pdf/1506.01497.pdf)
|
||||
|
||||
### R-FCN: 基於區域的全卷積網絡
|
||||
|
||||
這種算法比 Faster R-CNN 更快。主要思想如下:
|
||||
|
||||
1. 使用 ResNet-101 提取特徵。
|
||||
2. 特徵經過**位置敏感得分圖** (Position-Sensitive Score Map) 處理。每個來自 $C$ 類別的物件被劃分為 $k\times k$ 區域,我們訓練網絡來預測物件的部分。
|
||||
3. 對於 $k\times k$ 區域中的每個部分,所有網絡對物件類別進行投票,選擇得票最多的物件類別。
|
||||
|
||||

|
||||
|
||||
> 圖片來源:[官方論文](https://arxiv.org/abs/1605.06409)
|
||||
|
||||
### YOLO - You Only Look Once
|
||||
|
||||
YOLO 是一種實時單次通過算法。主要思想如下:
|
||||
|
||||
* 將圖片劃分為 $S\times S$ 區域。
|
||||
* 對於每個區域,**CNN** 預測 $n$ 個可能的物件、*邊界框*座標以及*置信度*=*概率* * IoU。
|
||||
|
||||

|
||||
|
||||
> 圖片來源:[官方論文](https://arxiv.org/abs/1506.02640)
|
||||
|
||||
### 其他算法
|
||||
|
||||
* RetinaNet: [官方論文](https://arxiv.org/abs/1708.02002)
|
||||
- [PyTorch 實現](https://pytorch.org/vision/stable/_modules/torchvision/models/detection/retinanet.html)
|
||||
- [Keras 實現](https://github.com/fizyr/keras-retinanet)
|
||||
- [Keras 示例中的 RetinaNet](https://keras.io/examples/vision/retinanet/)
|
||||
* SSD (單次檢測器): [官方論文](https://arxiv.org/abs/1512.02325)
|
||||
|
||||
## ✍️ 練習:物件偵測
|
||||
|
||||
繼續學習以下筆記本:
|
||||
|
||||
[ObjectDetection.ipynb](ObjectDetection.ipynb)
|
||||
|
||||
## 總結
|
||||
|
||||
在這節課中,你快速瀏覽了各種實現物件偵測的方法!
|
||||
|
||||
## 🚀 挑戰
|
||||
|
||||
閱讀以下關於 YOLO 的文章和筆記本,並嘗試自己實現:
|
||||
|
||||
* [優秀的部落格文章](https://www.analyticsvidhya.com/blog/2018/12/practical-guide-object-detection-yolo-framewor-python/) 描述 YOLO
|
||||
* [官方網站](https://pjreddie.com/darknet/yolo/)
|
||||
* YOLO: [Keras 實現](https://github.com/experiencor/keras-yolo2),[逐步筆記本](https://github.com/experiencor/basic-yolo-keras/blob/master/Yolo%20Step-by-Step.ipynb)
|
||||
* YOLO v2: [Keras 實現](https://github.com/experiencor/keras-yolo2),[逐步筆記本](https://github.com/experiencor/keras-yolo2/blob/master/Yolo%20Step-by-Step.ipynb)
|
||||
|
||||
## [課後測驗](https://ff-quizzes.netlify.app/en/ai/quiz/22)
|
||||
|
||||
## 回顧與自學
|
||||
|
||||
* [物件偵測](https://tjmachinelearning.com/lectures/1718/obj/) by Nikhil Sardana
|
||||
* [物件偵測算法的良好比較](https://lilianweng.github.io/lil-log/2018/12/27/object-detection-part-4.html)
|
||||
* [深度學習物件偵測算法回顧](https://medium.com/comet-app/review-of-deep-learning-algorithms-for-object-detection-c1f3d437b852)
|
||||
* [物件偵測算法的逐步介紹](https://www.analyticsvidhya.com/blog/2018/10/a-step-by-step-introduction-to-the-basic-object-detection-algorithms-part-1/)
|
||||
* [用 Python 實現 Faster R-CNN 進行物件偵測](https://www.analyticsvidhya.com/blog/2018/11/implementation-faster-r-cnn-python-object-detection/)
|
||||
|
||||
## [作業:物件偵測](lab/README.md)
|
||||
|
||||
---
|
||||
|
||||
|
|
@ -0,0 +1,66 @@
|
|||
# 使用 Hollywood Heads Dataset 進行頭部檢測
|
||||
|
||||
來自 [AI for Beginners Curriculum](https://github.com/microsoft/ai-for-beginners) 的實驗課題。
|
||||
|
||||
## 任務
|
||||
|
||||
在監控攝像機的視頻流中計算人數是一項重要的任務,這可以幫助我們估算商店的訪客數量、餐廳的繁忙時段等。為了解決這個問題,我們需要能夠從不同角度檢測人類的頭部。為了訓練物件檢測模型來檢測人類頭部,我們可以使用 [Hollywood Heads Dataset](https://www.di.ens.fr/willow/research/headdetection/)。
|
||||
|
||||
## 數據集
|
||||
|
||||
[Hollywood Heads Dataset](https://www.di.ens.fr/willow/research/headdetection/release/HollywoodHeads.zip) 包含 369,846 個人類頭部的標註,這些標註來自 224,740 張好萊塢電影的影格。數據集以 [https://host.robots.ox.ac.uk/pascal/VOC/](../../../../../../lessons/4-ComputerVision/11-ObjectDetection/lab/PASCAL VOC) 格式提供,每張圖片都有一個 XML 描述文件,其格式如下:
|
||||
|
||||
```xml
|
||||
<annotation>
|
||||
<folder>HollywoodHeads</folder>
|
||||
<filename>mov_021_149390.jpeg</filename>
|
||||
<source>
|
||||
<database>HollywoodHeads 2015 Database</database>
|
||||
<annotation>HollywoodHeads 2015</annotation>
|
||||
<image>WILLOW</image>
|
||||
</source>
|
||||
<size>
|
||||
<width>608</width>
|
||||
<height>320</height>
|
||||
<depth>3</depth>
|
||||
</size>
|
||||
<segmented>0</segmented>
|
||||
<object>
|
||||
<name>head</name>
|
||||
<bndbox>
|
||||
<xmin>201</xmin>
|
||||
<ymin>1</ymin>
|
||||
<xmax>480</xmax>
|
||||
<ymax>263</ymax>
|
||||
</bndbox>
|
||||
<difficult>0</difficult>
|
||||
</object>
|
||||
<object>
|
||||
<name>head</name>
|
||||
<bndbox>
|
||||
<xmin>3</xmin>
|
||||
<ymin>4</ymin>
|
||||
<xmax>241</xmax>
|
||||
<ymax>285</ymax>
|
||||
</bndbox>
|
||||
<difficult>0</difficult>
|
||||
</object>
|
||||
</annotation>
|
||||
```
|
||||
|
||||
在這個數據集中,只有一類物件 `head`,每個頭部都提供了邊界框的座標。你可以使用 Python 的庫來解析 XML,或者使用 [這個庫](https://pypi.org/project/pascal-voc/) 直接處理 PASCAL VOC 格式。
|
||||
|
||||
## 訓練物件檢測模型
|
||||
|
||||
你可以使用以下方法之一來訓練物件檢測模型:
|
||||
|
||||
* 使用 [Azure Custom Vision](https://docs.microsoft.com/azure/cognitive-services/custom-vision-service/quickstarts/object-detection?tabs=visual-studio&WT.mc_id=academic-77998-cacaste) 及其 Python API,在雲端以程式化方式訓練模型。Custom Vision 無法使用超過幾百張圖片來訓練模型,因此你可能需要限制數據集的大小。
|
||||
* 使用 [Keras 教程](https://keras.io/examples/vision/retinanet/) 中的範例來訓練 RetunaNet 模型。
|
||||
* 使用 [torchvision.models.detection.RetinaNet](https://pytorch.org/vision/stable/_modules/torchvision/models/detection/retinanet.html) 中的內建模組進行訓練。
|
||||
|
||||
## 重點
|
||||
|
||||
物件檢測是業界經常需要的一項任務。雖然有一些服務可以用來執行物件檢測(例如 [Azure Custom Vision](https://docs.microsoft.com/azure/cognitive-services/custom-vision-service/quickstarts/object-detection?tabs=visual-studio&WT.mc_id=academic-77998-cacaste)),但了解物件檢測的工作原理並能夠訓練自己的模型是非常重要的。
|
||||
|
||||
**免責聲明**:
|
||||
本文件已使用人工智能翻譯服務 [Co-op Translator](https://github.com/Azure/co-op-translator) 進行翻譯。雖然我們致力於提供準確的翻譯,但請注意,自動翻譯可能包含錯誤或不準確之處。原始語言的文件應被視為具權威性的來源。對於重要資訊,建議使用專業人工翻譯。我們對因使用此翻譯而引起的任何誤解或錯誤解釋概不負責。
|
||||
|
|
@ -0,0 +1,69 @@
|
|||
# 分割
|
||||
|
||||
我們之前學過物件偵測,它可以透過預測物件的*邊界框*來定位影像中的物件。然而,對於某些任務,我們不僅需要邊界框,還需要更精確的物件定位。這項任務稱為**分割**。
|
||||
|
||||
## [課前測驗](https://ff-quizzes.netlify.app/en/ai/quiz/23)
|
||||
|
||||
分割可以被視為**像素分類**,即對影像中的**每個**像素進行分類,並預測其類別(*背景*也是其中一個類別)。主要有兩種分割算法:
|
||||
|
||||
* **語義分割**僅告訴像素的類別,並不區分同一類別的不同物件。
|
||||
* **實例分割**將類別分成不同的實例。
|
||||
|
||||
例如,在實例分割中,這些羊是不同的物件;但在語義分割中,所有的羊都被表示為同一類別。
|
||||
|
||||
<img src="../../../../../translated_images/zh-HK/instance_vs_semantic.eee9812bebf8cd45.webp" width="50%">
|
||||
|
||||
> 圖片來源:[這篇部落格文章](https://nirmalamurali.medium.com/image-classification-vs-semantic-segmentation-vs-instance-segmentation-625c33a08d50)
|
||||
|
||||
有多種神經網絡架構可用於分割,但它們的結構都相似。某種程度上,它與你之前學過的自編碼器相似,但我們的目標不是解構原始影像,而是解構**遮罩**。因此,分割網絡包含以下部分:
|
||||
|
||||
* **編碼器**:從輸入影像中提取特徵。
|
||||
* **解碼器**:將這些特徵轉換為**遮罩影像**,其大小與輸入影像相同,通道數對應於類別數。
|
||||
|
||||
<img src="../../../../../translated_images/zh-HK/segm.92442f2cb42ff4fa.webp" width="80%">
|
||||
|
||||
> 圖片來源:[這篇出版物](https://arxiv.org/pdf/2001.05566.pdf)
|
||||
|
||||
特別需要提到的是分割中使用的損失函數。在使用傳統自編碼器時,我們需要測量兩個影像之間的相似性,可以使用均方誤差(MSE)來完成。在分割中,目標遮罩影像中的每個像素代表類別編號(在第三維度上進行獨熱編碼),因此我們需要使用特定於分類的損失函數——交叉熵損失,並對所有像素進行平均。如果遮罩是二元的,則使用**二元交叉熵損失**(BCE)。
|
||||
|
||||
> ✅ 獨熱編碼是一種將類別標籤編碼為向量的方法,其長度等於類別數。可以參考[這篇文章](https://datagy.io/sklearn-one-hot-encode/)了解這項技術。
|
||||
|
||||
## 醫學影像中的分割
|
||||
|
||||
在本課中,我們將通過訓練網絡來識別醫學影像中的人類痣(也稱為痣),以實現分割。我們將使用<a href="https://www.fc.up.pt/addi/ph2%20database.html">PH<sup>2</sup>資料庫</a>的皮膚鏡影像作為影像來源。該資料集包含200張影像,分為三類:典型痣、不典型痣和黑色素瘤。所有影像還包含相應的**遮罩**,用於勾勒痣的輪廓。
|
||||
|
||||
> ✅ 這項技術特別適合這類醫學影像,但你能想到其他現實世界中的應用嗎?
|
||||
|
||||
<img alt="navi" src="../../../../../translated_images/zh-HK/navi.2f20b727910110ea.webp"/>
|
||||
|
||||
> 圖片來源:PH<sup>2</sup>資料庫
|
||||
|
||||
我們將訓練一個模型,將任何痣從背景中分割出來。
|
||||
|
||||
## ✍️ 練習:語義分割
|
||||
|
||||
打開以下筆記本,了解不同的語義分割架構,練習使用它們,並觀察它們的實際效果。
|
||||
|
||||
* [語義分割 Pytorch](SemanticSegmentationPytorch.ipynb)
|
||||
* [語義分割 TensorFlow](SemanticSegmentationTF.ipynb)
|
||||
|
||||
## [課後測驗](https://ff-quizzes.netlify.app/en/ai/quiz/24)
|
||||
|
||||
## 結論
|
||||
|
||||
分割是一種非常強大的影像分類技術,超越了邊界框,實現了像素級分類。它在醫學影像等領域有著廣泛的應用。
|
||||
|
||||
## 🚀 挑戰
|
||||
|
||||
人體分割只是我們可以對人物影像進行的常見任務之一。其他重要任務包括**骨架偵測**和**姿態偵測**。試試[OpenPose](https://github.com/CMU-Perceptual-Computing-Lab/openpose)庫,看看姿態偵測的應用。
|
||||
|
||||
## 回顧與自學
|
||||
|
||||
這篇[維基百科文章](https://wikipedia.org/wiki/Image_segmentation)提供了該技術的各種應用概述。自行了解該領域中的實例分割和全景分割的子領域。
|
||||
|
||||
## [作業](lab/README.md)
|
||||
|
||||
在本次實驗中,使用 [Segmentation Full Body MADS Dataset](https://www.kaggle.com/datasets/tapakah68/segmentation-full-body-mads-dataset) 資料集,嘗試進行**人體分割**。
|
||||
|
||||
---
|
||||
|
||||
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
|
|
@ -0,0 +1,22 @@
|
|||
# 人體分割
|
||||
|
||||
來自 [AI for Beginners Curriculum](https://github.com/microsoft/ai-for-beginners) 的實驗作業。
|
||||
|
||||
## 任務
|
||||
|
||||
在視頻製作中,例如天氣預報,我們經常需要將攝像機中的人像剪裁出來,並將其放置在其他畫面上。這通常是通過 **色度鍵** 技術來完成的,當人站在一個統一顏色的背景前拍攝時,該背景會被移除。在這個實驗中,我們將訓練一個神經網絡模型來剪裁出人像輪廓。
|
||||
|
||||
## 數據集
|
||||
|
||||
我們將使用來自 Kaggle 的 [Segmentation Full Body MADS Dataset](https://www.kaggle.com/datasets/tapakah68/segmentation-full-body-mads-dataset)。請從 Kaggle 手動下載該數據集。
|
||||
|
||||
## 起始筆記本
|
||||
|
||||
通過打開 [BodySegmentation.ipynb](../../../../../../lessons/4-ComputerVision/12-Segmentation/lab/BodySegmentation.ipynb) 開始這個實驗。
|
||||
|
||||
## 收穫
|
||||
|
||||
人體分割只是我們可以對人物圖像進行的常見任務之一。其他重要的任務還包括 **骨架檢測** 和 **姿勢檢測**。可以查看 [OpenPose](https://github.com/CMU-Perceptual-Computing-Lab/openpose) 庫,了解如何實現這些任務。
|
||||
|
||||
**免責聲明**:
|
||||
本文件已使用人工智能翻譯服務 [Co-op Translator](https://github.com/Azure/co-op-translator) 進行翻譯。我們致力於提供準確的翻譯,但請注意,自動翻譯可能包含錯誤或不準確之處。應以原文文件作為權威來源。如涉及關鍵資訊,建議尋求專業人工翻譯。我們對因使用此翻譯而引起的任何誤解或誤釋概不負責。
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
# 電腦視覺
|
||||
|
||||

|
||||
|
||||
在這部分,我們將學習以下內容:
|
||||
|
||||
* [電腦視覺與 OpenCV 簡介](06-IntroCV/README.md)
|
||||
* [卷積神經網絡](07-ConvNets/README.md)
|
||||
* [預訓練網絡與遷移學習](08-TransferLearning/README.md)
|
||||
* [自編碼器](09-Autoencoders/README.md)
|
||||
* [生成對抗網絡](10-GANs/README.md)
|
||||
* [物件偵測](11-ObjectDetection/README.md)
|
||||
* [語義分割](12-Segmentation/README.md)
|
||||
|
||||
**免責聲明**:
|
||||
本文件已使用人工智能翻譯服務 [Co-op Translator](https://github.com/Azure/co-op-translator) 進行翻譯。我們致力於提供準確的翻譯,但請注意,自動翻譯可能包含錯誤或不準確之處。應以原文文件作為權威來源。對於關鍵資訊,建議尋求專業人工翻譯。我們對因使用此翻譯而引起的任何誤解或錯誤解釋概不負責。
|
||||
|
|
@ -0,0 +1,76 @@
|
|||
# 將文字表示為張量
|
||||
|
||||
## [課前測驗](https://ff-quizzes.netlify.app/en/ai/quiz/25)
|
||||
|
||||
## 文字分類
|
||||
|
||||
在本部分的第一部分中,我們將專注於**文字分類**任務。我們將使用 [AG News](https://www.kaggle.com/amananandrai/ag-news-classification-dataset) 數據集,其中包含以下類型的新聞文章:
|
||||
|
||||
* 類別:科學/技術
|
||||
* 標題:Ky. 公司獲得研究肽的資助 (AP)
|
||||
* 內容:AP - 一家由路易斯維爾大學化學研究員創立的公司獲得了一項資助,用於開發...
|
||||
|
||||
我們的目標是根據文本將新聞項目分類到其中一個類別中。
|
||||
|
||||
## 表示文字
|
||||
|
||||
如果我們希望使用神經網絡解決自然語言處理 (NLP) 任務,我們需要某種方式將文字表示為張量。電腦已經使用像 ASCII 或 UTF-8 這樣的編碼將文本字符表示為映射到螢幕字體的數字。
|
||||
|
||||
<img alt="顯示字符映射到 ASCII 和二進制表示的圖示" src="../../../../../translated_images/zh-HK/ascii-character-map.18ed6aa7f3b0a7ff.webp" width="50%"/>
|
||||
|
||||
> [圖片來源](https://www.seobility.net/en/wiki/ASCII)
|
||||
|
||||
作為人類,我們理解每個字母**代表**什麼,以及所有字符如何組合形成句子的單詞。然而,電腦本身並不具備這種理解能力,神經網絡需要在訓練過程中學習其含義。
|
||||
|
||||
因此,我們可以使用不同的方法來表示文字:
|
||||
|
||||
* **字符級表示**,即將每個字符視為一個數字來表示文字。假設我們的文本語料庫中有 *C* 個不同的字符,單詞 *Hello* 將被表示為 5x*C* 的張量。每個字母在獨熱編碼中對應一個張量列。
|
||||
* **單詞級表示**,即創建一個包含文本中所有單詞的**詞彙表**,然後使用獨熱編碼表示單詞。這種方法相對更好,因為單個字母本身並沒有太多意義,因此通過使用更高層次的語義概念——單詞——我們簡化了神經網絡的任務。然而,由於詞典的尺寸較大,我們需要處理高維稀疏張量。
|
||||
|
||||
無論使用哪種表示方法,我們首先需要將文字轉換為**標記**序列,每個標記可以是字符、單詞,甚至是單詞的一部分。接著,我們使用**詞彙表**將標記轉換為數字,通常使用獨熱編碼,然後將這些數字輸入到神經網絡中。
|
||||
|
||||
## N-Grams
|
||||
|
||||
在自然語言中,單詞的精確含義只能在上下文中確定。例如,*neural network* 和 *fishing network* 的含義完全不同。考慮上下文的一種方法是基於單詞對構建模型,並將單詞對視為獨立的詞彙標記。這樣,句子 *I like to go fishing* 將被表示為以下標記序列:*I like*、*like to*、*to go*、*go fishing*。這種方法的問題在於詞典尺寸顯著增大,並且像 *go fishing* 和 *go shopping* 這樣的組合被表示為不同的標記,儘管它們使用了相同的動詞,但並不共享任何語義相似性。
|
||||
|
||||
在某些情況下,我們可能會考慮使用三元組(tri-grams)——三個單詞的組合。因此,這種方法通常被稱為**n-grams**。此外,使用字符級表示時,n-grams 大致對應於不同的音節。
|
||||
|
||||
## Bag-of-Words 和 TF/IDF
|
||||
|
||||
在解決像文字分類這樣的任務時,我們需要能夠用一個固定大小的向量表示文字,該向量將作為最終密集分類器的輸入。一種最簡單的方法是將所有單詞的個別表示結合起來,例如通過相加。如果我們將每個單詞的獨熱編碼相加,最終會得到一個頻率向量,顯示每個單詞在文本中出現的次數。這種文字表示方法被稱為**詞袋** (BoW)。
|
||||
|
||||
<img src="../../../../../translated_images/zh-HK/bow.3811869cff59368d.webp" width="90%"/>
|
||||
|
||||
> 圖片由作者提供
|
||||
|
||||
BoW 本質上表示了哪些單詞出現在文本中以及它們的數量,這確實可以很好地指示文本的主題。例如,關於政治的新聞文章可能包含像 *president* 和 *country* 這樣的單詞,而科學出版物可能包含像 *collider*、*discovered* 等單詞。因此,單詞頻率在許多情況下可以很好地指示文本內容。
|
||||
|
||||
BoW 的問題在於某些常見單詞,例如 *and*、*is* 等,出現在大多數文本中,並且它們的頻率最高,掩蓋了真正重要的單詞。我們可以通過考慮單詞在整個文檔集合中出現的頻率來降低這些單詞的重要性。這就是 TF/IDF 方法的主要思想,該方法在本課程附帶的筆記本中有更詳細的介紹。
|
||||
|
||||
然而,這些方法都無法完全考慮文字的**語義**。我們需要更強大的神經網絡模型來做到這一點,這將在本部分後續內容中討論。
|
||||
|
||||
## ✍️ 練習:文字表示
|
||||
|
||||
在以下筆記本中繼續學習:
|
||||
|
||||
* [使用 PyTorch 表示文字](TextRepresentationPyTorch.ipynb)
|
||||
* [使用 TensorFlow 表示文字](TextRepresentationTF.ipynb)
|
||||
|
||||
## 結論
|
||||
|
||||
到目前為止,我們已經研究了可以為不同單詞添加頻率權重的技術。然而,它們無法表示含義或順序。正如著名語言學家 J. R. Firth 在1935年所說:“單詞的完整含義總是與上下文相關,任何脫離上下文的含義研究都不能被認真對待。”我們將在課程後續內容中學習如何通過語言建模從文本中捕捉上下文信息。
|
||||
|
||||
## 🚀 挑戰
|
||||
|
||||
嘗試使用詞袋和不同的數據模型進行其他練習。你可能會從這個 [Kaggle 比賽](https://www.kaggle.com/competitions/word2vec-nlp-tutorial/overview/part-1-for-beginners-bag-of-words) 中獲得靈感。
|
||||
|
||||
## [課後測驗](https://ff-quizzes.netlify.app/en/ai/quiz/26)
|
||||
|
||||
## 回顧與自學
|
||||
|
||||
在 [Microsoft Learn](https://docs.microsoft.com/learn/modules/intro-natural-language-processing-pytorch/?WT.mc_id=academic-77998-cacaste) 上練習你的文字嵌入和詞袋技術技能。
|
||||
|
||||
## [作業:筆記本](assignment.md)
|
||||
|
||||
---
|
||||
|
||||
|
|
@ -0,0 +1,577 @@
|
|||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"# 文本分類任務\n",
|
||||
"\n",
|
||||
"如前所述,我們將專注於基於 **AG_NEWS** 數據集的簡單文本分類任務,目的是將新聞標題分類為以下四個類別之一:國際、體育、商業和科學/技術。\n",
|
||||
"\n",
|
||||
"## 數據集\n",
|
||||
"\n",
|
||||
"此數據集已內建於 [`torchtext`](https://github.com/pytorch/text) 模組中,因此我們可以輕鬆存取它。\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 1,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import torch\n",
|
||||
"import torchtext\n",
|
||||
"import os\n",
|
||||
"import collections\n",
|
||||
"os.makedirs('./data',exist_ok=True)\n",
|
||||
"train_dataset, test_dataset = torchtext.datasets.AG_NEWS(root='./data')\n",
|
||||
"classes = ['World', 'Sports', 'Business', 'Sci/Tech']"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"在這裡,`train_dataset` 和 `test_dataset` 包含分別返回標籤(類別編號)和文本的集合,例如:\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 2,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"(3,\n",
|
||||
" \"Wall St. Bears Claw Back Into the Black (Reuters) Reuters - Short-sellers, Wall Street's dwindling\\\\band of ultra-cynics, are seeing green again.\")"
|
||||
]
|
||||
},
|
||||
"execution_count": 2,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"list(train_dataset)[0]"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"所以,讓我們列印出資料集中的前10個新標題:\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 5,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"**Sci/Tech** -> Wall St. Bears Claw Back Into the Black (Reuters) Reuters - Short-sellers, Wall Street's dwindling\\band of ultra-cynics, are seeing green again.\n",
|
||||
"**Sci/Tech** -> Carlyle Looks Toward Commercial Aerospace (Reuters) Reuters - Private investment firm Carlyle Group,\\which has a reputation for making well-timed and occasionally\\controversial plays in the defense industry, has quietly placed\\its bets on another part of the market.\n",
|
||||
"**Sci/Tech** -> Oil and Economy Cloud Stocks' Outlook (Reuters) Reuters - Soaring crude prices plus worries\\about the economy and the outlook for earnings are expected to\\hang over the stock market next week during the depth of the\\summer doldrums.\n",
|
||||
"**Sci/Tech** -> Iraq Halts Oil Exports from Main Southern Pipeline (Reuters) Reuters - Authorities have halted oil export\\flows from the main pipeline in southern Iraq after\\intelligence showed a rebel militia could strike\\infrastructure, an oil official said on Saturday.\n",
|
||||
"**Sci/Tech** -> Oil prices soar to all-time record, posing new menace to US economy (AFP) AFP - Tearaway world oil prices, toppling records and straining wallets, present a new economic menace barely three months before the US presidential elections.\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"for i,x in zip(range(5),train_dataset):\n",
|
||||
" print(f\"**{classes[x[0]]}** -> {x[1]}\")\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"因為數據集是迭代器,如果我們想多次使用數據,我們需要將其轉換為列表:\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 3,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"train_dataset, test_dataset = torchtext.datasets.AG_NEWS(root='./data')\n",
|
||||
"train_dataset = list(train_dataset)\n",
|
||||
"test_dataset = list(test_dataset)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## 分詞\n",
|
||||
"\n",
|
||||
"現在我們需要將文本轉換成**數字**,以便表示為張量。如果我們想要詞級表示,需要完成以下兩件事:\n",
|
||||
"* 使用**分詞器**將文本拆分成**詞元**\n",
|
||||
"* 建立這些詞元的**詞彙表**。\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 4,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"['he', 'said', 'hello']"
|
||||
]
|
||||
},
|
||||
"execution_count": 4,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"tokenizer = torchtext.data.utils.get_tokenizer('basic_english')\n",
|
||||
"tokenizer('He said: hello')"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 5,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"counter = collections.Counter()\n",
|
||||
"for (label, line) in train_dataset:\n",
|
||||
" counter.update(tokenizer(line))\n",
|
||||
"vocab = torchtext.vocab.vocab(counter, min_freq=1)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"使用詞彙表,我們可以輕鬆地將標記化的字串編碼為一組數字:\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 19,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"Vocab size if 95810\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"[599, 3279, 97, 1220, 329, 225, 7368]"
|
||||
]
|
||||
},
|
||||
"execution_count": 19,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"vocab_size = len(vocab)\n",
|
||||
"print(f\"Vocab size if {vocab_size}\")\n",
|
||||
"\n",
|
||||
"stoi = vocab.get_stoi() # dict to convert tokens to indices\n",
|
||||
"\n",
|
||||
"def encode(x):\n",
|
||||
" return [stoi[s] for s in tokenizer(x)]\n",
|
||||
"\n",
|
||||
"encode('I love to play with my words')"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## 詞袋文字表示法\n",
|
||||
"\n",
|
||||
"由於文字代表意義,有時我們可以僅透過查看個別的文字來理解文本的含義,而不需要考慮它們在句子中的順序。例如,在分類新聞時,像 *天氣*、*雪* 這些詞可能表明是 *天氣預報*,而像 *股票*、*美元* 則可能屬於 *財經新聞*。\n",
|
||||
"\n",
|
||||
"**詞袋** (BoW) 向量表示法是最常用的傳統向量表示法之一。每個文字都與向量索引相關聯,向量元素包含某個文字在特定文件中出現的次數。\n",
|
||||
"\n",
|
||||
" \n",
|
||||
"\n",
|
||||
"> **注意**:你也可以將 BoW 理解為文本中每個文字的獨立一熱編碼向量的總和。\n",
|
||||
"\n",
|
||||
"以下是一個使用 Scikit Learn Python 庫生成詞袋表示法的示例:\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 7,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"array([[1, 1, 0, 2, 0, 0, 0, 0, 0]], dtype=int64)"
|
||||
]
|
||||
},
|
||||
"execution_count": 7,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"from sklearn.feature_extraction.text import CountVectorizer\n",
|
||||
"vectorizer = CountVectorizer()\n",
|
||||
"corpus = [\n",
|
||||
" 'I like hot dogs.',\n",
|
||||
" 'The dog ran fast.',\n",
|
||||
" 'Its hot outside.',\n",
|
||||
" ]\n",
|
||||
"vectorizer.fit_transform(corpus)\n",
|
||||
"vectorizer.transform(['My dog likes hot dogs on a hot day.']).toarray()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"要從我們的 AG_NEWS 數據集的向量表示計算詞袋向量,可以使用以下函數:\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 20,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"tensor([2., 1., 2., ..., 0., 0., 0.])\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"vocab_size = len(vocab)\n",
|
||||
"\n",
|
||||
"def to_bow(text,bow_vocab_size=vocab_size):\n",
|
||||
" res = torch.zeros(bow_vocab_size,dtype=torch.float32)\n",
|
||||
" for i in encode(text):\n",
|
||||
" if i<bow_vocab_size:\n",
|
||||
" res[i] += 1\n",
|
||||
" return res\n",
|
||||
"\n",
|
||||
"print(to_bow(train_dataset[0][1]))"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"> **注意:** 這裡我們使用全域變數 `vocab_size` 來指定詞彙表的預設大小。由於詞彙表的大小通常相當大,我們可以將詞彙表的大小限制為最常見的詞彙。嘗試降低 `vocab_size` 的值並運行以下程式碼,觀察它如何影響準確性。你應該預期會有一些準確性的下降,但不會太劇烈,以換取更高的效能。\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## 訓練 BoW 分類器\n",
|
||||
"\n",
|
||||
"現在我們已經學會如何建立文字的詞袋表示法,接下來讓我們在此基礎上訓練一個分類器。首先,我們需要將數據集轉換為適合訓練的格式,將所有位置向量表示法轉換為詞袋表示法。這可以通過將 `bowify` 函數作為標準 torch `DataLoader` 的 `collate_fn` 參數來實現:\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 21,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from torch.utils.data import DataLoader\n",
|
||||
"import numpy as np \n",
|
||||
"\n",
|
||||
"# this collate function gets list of batch_size tuples, and needs to \n",
|
||||
"# return a pair of label-feature tensors for the whole minibatch\n",
|
||||
"def bowify(b):\n",
|
||||
" return (\n",
|
||||
" torch.LongTensor([t[0]-1 for t in b]),\n",
|
||||
" torch.stack([to_bow(t[1]) for t in b])\n",
|
||||
" )\n",
|
||||
"\n",
|
||||
"train_loader = DataLoader(train_dataset, batch_size=16, collate_fn=bowify, shuffle=True)\n",
|
||||
"test_loader = DataLoader(test_dataset, batch_size=16, collate_fn=bowify, shuffle=True)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"現在讓我們定義一個簡單的分類器神經網絡,其中包含一個線性層。輸入向量的大小等於 `vocab_size`,輸出大小對應於類別數量(4)。由於我們正在解決分類任務,最終的激活函數是 `LogSoftmax()`。\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 22,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"net = torch.nn.Sequential(torch.nn.Linear(vocab_size,4),torch.nn.LogSoftmax(dim=1))"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"現在我們將定義標準的 PyTorch 訓練循環。由於我們的數據集相當大,為了教學目的,我們只會訓練一個 epoch,有時甚至少於一個 epoch(通過指定 `epoch_size` 參數可以限制訓練)。我們還會在訓練過程中報告累積的訓練準確率;報告的頻率是通過 `report_freq` 參數指定的。\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 24,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"def train_epoch(net,dataloader,lr=0.01,optimizer=None,loss_fn = torch.nn.NLLLoss(),epoch_size=None, report_freq=200):\n",
|
||||
" optimizer = optimizer or torch.optim.Adam(net.parameters(),lr=lr)\n",
|
||||
" net.train()\n",
|
||||
" total_loss,acc,count,i = 0,0,0,0\n",
|
||||
" for labels,features in dataloader:\n",
|
||||
" optimizer.zero_grad()\n",
|
||||
" out = net(features)\n",
|
||||
" loss = loss_fn(out,labels) #cross_entropy(out,labels)\n",
|
||||
" loss.backward()\n",
|
||||
" optimizer.step()\n",
|
||||
" total_loss+=loss\n",
|
||||
" _,predicted = torch.max(out,1)\n",
|
||||
" acc+=(predicted==labels).sum()\n",
|
||||
" count+=len(labels)\n",
|
||||
" i+=1\n",
|
||||
" if i%report_freq==0:\n",
|
||||
" print(f\"{count}: acc={acc.item()/count}\")\n",
|
||||
" if epoch_size and count>epoch_size:\n",
|
||||
" break\n",
|
||||
" return total_loss.item()/count, acc.item()/count"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 25,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"3200: acc=0.8028125\n",
|
||||
"6400: acc=0.8371875\n",
|
||||
"9600: acc=0.8534375\n",
|
||||
"12800: acc=0.85765625\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"(0.026090790722161722, 0.8620069296375267)"
|
||||
]
|
||||
},
|
||||
"execution_count": 25,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"train_epoch(net,train_loader,epoch_size=15000)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## BiGrams、TriGrams 和 N-Grams\n",
|
||||
"\n",
|
||||
"袋裝詞語(Bag of Words)方法的一個限制是,有些詞語是多詞組成的表達,例如「hot dog」這個詞語的意思與「hot」和「dog」在其他語境中的意思完全不同。如果我們總是用相同的向量來表示「hot」和「dog」,可能會讓模型感到混淆。\n",
|
||||
"\n",
|
||||
"為了解決這個問題,**N-gram 表示法**經常被用於文件分類的方法中,其中每個單詞、雙詞或三詞的出現頻率是訓練分類器的一個有用特徵。例如,在雙詞組(bigram)表示法中,除了原本的單詞外,我們還會將所有的詞對加入詞彙表中。\n",
|
||||
"\n",
|
||||
"以下是一個使用 Scikit Learn 生成雙詞組袋裝詞語表示法的例子:\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 26,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"Vocabulary:\n",
|
||||
" {'i': 7, 'like': 11, 'hot': 4, 'dogs': 2, 'i like': 8, 'like hot': 12, 'hot dogs': 5, 'the': 16, 'dog': 0, 'ran': 14, 'fast': 3, 'the dog': 17, 'dog ran': 1, 'ran fast': 15, 'its': 9, 'outside': 13, 'its hot': 10, 'hot outside': 6}\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"array([[1, 0, 1, 0, 2, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]],\n",
|
||||
" dtype=int64)"
|
||||
]
|
||||
},
|
||||
"execution_count": 26,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"bigram_vectorizer = CountVectorizer(ngram_range=(1, 2), token_pattern=r'\\b\\w+\\b', min_df=1)\n",
|
||||
"corpus = [\n",
|
||||
" 'I like hot dogs.',\n",
|
||||
" 'The dog ran fast.',\n",
|
||||
" 'Its hot outside.',\n",
|
||||
" ]\n",
|
||||
"bigram_vectorizer.fit_transform(corpus)\n",
|
||||
"print(\"Vocabulary:\\n\",bigram_vectorizer.vocabulary_)\n",
|
||||
"bigram_vectorizer.transform(['My dog likes hot dogs on a hot day.']).toarray()\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"N-gram 方法的主要缺點是詞彙量會迅速增長。在實際應用中,我們需要將 N-gram 表示法與一些降維技術結合使用,例如 *嵌入*,我們會在下一單元中討論。\n",
|
||||
"\n",
|
||||
"要在我們的 **AG News** 數據集中使用 N-gram 表示法,我們需要建立專門的 ngram 詞彙:\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 27,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"Bigram vocabulary length = 1308842\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"counter = collections.Counter()\n",
|
||||
"for (label, line) in train_dataset:\n",
|
||||
" l = tokenizer(line)\n",
|
||||
" counter.update(torchtext.data.utils.ngrams_iterator(l,ngrams=2))\n",
|
||||
" \n",
|
||||
"bi_vocab = torchtext.vocab.vocab(counter, min_freq=1)\n",
|
||||
"\n",
|
||||
"print(\"Bigram vocabulary length = \",len(bi_vocab))"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"我們可以使用上述相同的程式碼來訓練分類器,但這樣會非常耗費記憶體。在下一個單元中,我們將使用嵌入來訓練二元語法分類器。\n",
|
||||
"\n",
|
||||
"> **注意:** 你只能保留那些在文本中出現次數超過指定數量的 ngrams。這樣可以確保罕見的二元語法會被省略,並顯著減少維度。為此,將 `min_freq` 參數設置為更高的值,並觀察詞彙表長度的變化。\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## 詞頻-逆文檔頻率 TF-IDF\n",
|
||||
"\n",
|
||||
"在 BoW 表示法中,詞語的出現次數被均等地加權,無論詞語本身的特性如何。然而,很明顯像 *a*、*in* 等這些常見詞對分類的作用遠不如一些專業術語。事實上,在大多數 NLP 任務中,有些詞語比其他詞語更具相關性。\n",
|
||||
"\n",
|
||||
"**TF-IDF** 代表 **詞頻–逆文檔頻率**。它是袋子模型(BoW)的變體,與使用二進制 0/1 值來表示詞語是否出現在文檔中的方法不同,TF-IDF 使用浮點值,該值與詞語在語料庫中的出現頻率相關。\n",
|
||||
"\n",
|
||||
"更正式地說,詞語 $i$ 在文檔 $j$ 中的權重 $w_{ij}$ 定義如下:\n",
|
||||
"$$\n",
|
||||
"w_{ij} = tf_{ij}\\times\\log({N\\over df_i})\n",
|
||||
"$$\n",
|
||||
"其中:\n",
|
||||
"* $tf_{ij}$ 是詞語 $i$ 在文檔 $j$ 中的出現次數,即我們之前看到的 BoW 值\n",
|
||||
"* $N$ 是語料庫中的文檔總數\n",
|
||||
"* $df_i$ 是包含詞語 $i$ 的文檔數量\n",
|
||||
"\n",
|
||||
"TF-IDF 值 $w_{ij}$ 與詞語在文檔中出現的次數成正比,但會根據語料庫中包含該詞語的文檔數量進行調整,這有助於平衡某些詞語出現頻率較高的情況。例如,如果某個詞語出現在語料庫中的 *每一份* 文檔中,則 $df_i=N$,而 $w_{ij}=0$,這些詞語將被完全忽略。\n",
|
||||
"\n",
|
||||
"你可以使用 Scikit Learn 輕鬆地創建文本的 TF-IDF 向量化:\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 28,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"array([[0.43381609, 0. , 0.43381609, 0. , 0.65985664,\n",
|
||||
" 0.43381609, 0. , 0. , 0. , 0. ,\n",
|
||||
" 0. , 0. , 0. , 0. , 0. ,\n",
|
||||
" 0. ]])"
|
||||
]
|
||||
},
|
||||
"execution_count": 28,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"from sklearn.feature_extraction.text import TfidfVectorizer\n",
|
||||
"vectorizer = TfidfVectorizer(ngram_range=(1,2))\n",
|
||||
"vectorizer.fit_transform(corpus)\n",
|
||||
"vectorizer.transform(['My dog likes hot dogs on a hot day.']).toarray()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## 結論\n",
|
||||
"\n",
|
||||
"然而,儘管 TF-IDF 表示法為不同的詞語提供了頻率權重,但它無法表達詞語的意義或順序。正如著名語言學家 J. R. Firth 在1935年所說:「詞語的完整意義總是與上下文相關,任何脫離上下文的意義研究都不應被認真對待。」我們稍後會在課程中學習如何通過語言建模從文本中捕捉上下文信息。\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"\n---\n\n**免責聲明**: \n本文件已使用人工智能翻譯服務 [Co-op Translator](https://github.com/Azure/co-op-translator) 進行翻譯。我們致力於提供準確的翻譯,但請注意,自動翻譯可能包含錯誤或不準確之處。應以原文文件作為權威來源。對於關鍵資訊,建議尋求專業人工翻譯。我們對因使用此翻譯而引起的任何誤解或誤釋不承擔責任。\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"interpreter": {
|
||||
"hash": "16af2a8bbb083ea23e5e41c7f5787656b2ce26968575d8763f2c4b17f9cd711f"
|
||||
},
|
||||
"kernelspec": {
|
||||
"display_name": "Python 3.8.12 ('py38')",
|
||||
"language": "python",
|
||||
"name": "python3"
|
||||
},
|
||||
"language_info": {
|
||||
"codemirror_mode": {
|
||||
"name": "ipython",
|
||||
"version": 3
|
||||
},
|
||||
"file_extension": ".py",
|
||||
"mimetype": "text/x-python",
|
||||
"name": "python",
|
||||
"nbconvert_exporter": "python",
|
||||
"pygments_lexer": "ipython3",
|
||||
"version": "3.8.12"
|
||||
},
|
||||
"coopTranslator": {
|
||||
"original_hash": "7b9040985e748e4e2d4c689892456ad7",
|
||||
"translation_date": "2025-08-31T10:59:02+00:00",
|
||||
"source_file": "lessons/5-NLP/13-TextRep/TextRepresentationPyTorch.ipynb",
|
||||
"language_code": "hk"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 2
|
||||
}
|
||||
|
|
@ -0,0 +1,647 @@
|
|||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"# 文本分類任務\n",
|
||||
"\n",
|
||||
"在這個模組中,我們將從一個簡單的文本分類任務開始,基於 **[AG_NEWS](http://www.di.unipi.it/~gulli/AG_corpus_of_news_articles.html)** 數據集:我們會將新聞標題分類為以下四個類別之一:世界、體育、商業和科技。\n",
|
||||
"\n",
|
||||
"## 數據集\n",
|
||||
"\n",
|
||||
"為了載入數據集,我們將使用 **[TensorFlow Datasets](https://www.tensorflow.org/datasets)** API。\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 1,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import tensorflow as tf\n",
|
||||
"from tensorflow import keras\n",
|
||||
"import tensorflow_datasets as tfds\n",
|
||||
"\n",
|
||||
"# In this tutorial, we will be training a lot of models. In order to use GPU memory cautiously,\n",
|
||||
"# we will set tensorflow option to grow GPU memory allocation when required.\n",
|
||||
"physical_devices = tf.config.list_physical_devices('GPU') \n",
|
||||
"if len(physical_devices)>0:\n",
|
||||
" tf.config.experimental.set_memory_growth(physical_devices[0], True)\n",
|
||||
"\n",
|
||||
"dataset = tfds.load('ag_news_subset')"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"我們現在可以分別使用 `dataset['train']` 和 `dataset['test']` 來訪問數據集的訓練和測試部分:\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 3,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"Length of train dataset = 120000\n",
|
||||
"Length of test dataset = 7600\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"ds_train = dataset['train']\n",
|
||||
"ds_test = dataset['test']\n",
|
||||
"\n",
|
||||
"print(f\"Length of train dataset = {len(ds_train)}\")\n",
|
||||
"print(f\"Length of test dataset = {len(ds_test)}\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"讓我們列印出資料集中前10個新的標題:\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 4,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"3 (Sci/Tech) -> b'AMD Debuts Dual-Core Opteron Processor' b'AMD #39;s new dual-core Opteron chip is designed mainly for corporate computing applications, including databases, Web services, and financial transactions.'\n",
|
||||
"1 (Sports) -> b\"Wood's Suspension Upheld (Reuters)\" b'Reuters - Major League Baseball\\\\Monday announced a decision on the appeal filed by Chicago Cubs\\\\pitcher Kerry Wood regarding a suspension stemming from an\\\\incident earlier this season.'\n",
|
||||
"2 (Business) -> b'Bush reform may have blue states seeing red' b'President Bush #39;s quot;revenue-neutral quot; tax reform needs losers to balance its winners, and people claiming the federal deduction for state and local taxes may be in administration planners #39; sights, news reports say.'\n",
|
||||
"3 (Sci/Tech) -> b\"'Halt science decline in schools'\" b'Britain will run out of leading scientists unless science education is improved, says Professor Colin Pillinger.'\n",
|
||||
"1 (Sports) -> b'Gerrard leaves practice' b'London, England (Sports Network) - England midfielder Steven Gerrard injured his groin late in Thursday #39;s training session, but is hopeful he will be ready for Saturday #39;s World Cup qualifier against Austria.'\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"classes = ['World', 'Sports', 'Business', 'Sci/Tech']\n",
|
||||
"\n",
|
||||
"for i,x in zip(range(5),ds_train):\n",
|
||||
" print(f\"{x['label']} ({classes[x['label']]}) -> {x['title']} {x['description']}\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## 文本向量化\n",
|
||||
"\n",
|
||||
"現在我們需要將文本轉換成可以表示為張量的**數字**。如果我們想要詞級別的表示,需要完成以下兩件事:\n",
|
||||
"\n",
|
||||
"* 使用**分詞器**將文本拆分為**詞元**。\n",
|
||||
"* 建立這些詞元的**詞彙表**。\n",
|
||||
"\n",
|
||||
"### 限制詞彙表大小\n",
|
||||
"\n",
|
||||
"在 AG News 數據集的例子中,詞彙表的大小相當大,超過 100k 個單詞。一般來說,我們不需要那些在文本中很少出現的單詞——只有少數句子會包含它們,而模型無法從中學習。因此,通過向向量化器構造函數傳遞參數,將詞彙表的大小限制為較小的數量是有意義的:\n",
|
||||
"\n",
|
||||
"這兩個步驟都可以使用 **TextVectorization** 層來處理。我們來實例化向量化器對象,然後調用 `adapt` 方法來遍歷所有文本並建立詞彙表:\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 5,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"vocab_size = 50000\n",
|
||||
"vectorizer = keras.layers.experimental.preprocessing.TextVectorization(max_tokens=vocab_size)\n",
|
||||
"vectorizer.adapt(ds_train.take(500).map(lambda x: x['title']+' '+x['description']))"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"> **注意** 我們只使用整個數據集的一部分來建立詞彙表。這樣做是為了加快執行速度,避免讓你等待太久。然而,我們承擔了一些風險,即整個數據集中的某些詞可能不會被包含在詞彙表中,並在訓練過程中被忽略。因此,使用完整的詞彙表大小並在 `adapt` 過程中遍歷整個數據集應該可以提高最終的準確性,但提升幅度不會太大。\n",
|
||||
"\n",
|
||||
"現在我們可以訪問實際的詞彙表:\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 6,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"['', '[UNK]', 'the', 'to', 'a', 'in', 'of', 'and', 'on', 'for']\n",
|
||||
"Length of vocabulary: 5335\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"vocab = vectorizer.get_vocabulary()\n",
|
||||
"vocab_size = len(vocab)\n",
|
||||
"print(vocab[:10])\n",
|
||||
"print(f\"Length of vocabulary: {vocab_size}\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"使用向量化器,我們可以輕鬆地將任何文本編碼為一組數字:\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 7,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"<tf.Tensor: shape=(7,), dtype=int64, numpy=array([ 112, 3695, 3, 304, 11, 1041, 1], dtype=int64)>"
|
||||
]
|
||||
},
|
||||
"execution_count": 7,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"vectorizer('I love to play with my words')"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## 詞袋(Bag-of-words)文本表示法\n",
|
||||
"\n",
|
||||
"由於文字代表了意義,有時我們可以僅通過查看單個詞語來理解一段文字的意思,而不需要考慮句子中的詞序。例如,在分類新聞時,像 *weather* 和 *snow* 這樣的詞語可能表明是 *天氣預報*,而像 *stocks* 和 *dollar* 則可能屬於 *財經新聞*。\n",
|
||||
"\n",
|
||||
"**詞袋**(BoW)向量表示法是最容易理解的傳統向量表示法。每個詞語都對應一個向量索引,而向量中的元素則表示某個詞語在特定文檔中出現的次數。\n",
|
||||
"\n",
|
||||
" \n",
|
||||
"\n",
|
||||
"> **Note**: 你也可以將 BoW 理解為文本中每個詞語的單熱編碼(one-hot-encoded)向量的總和。\n",
|
||||
"\n",
|
||||
"以下是一個使用 Scikit Learn Python 庫生成詞袋表示法的範例:\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 8,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"array([[1, 1, 0, 2, 0, 0, 0, 0, 0]], dtype=int64)"
|
||||
]
|
||||
},
|
||||
"execution_count": 8,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"from sklearn.feature_extraction.text import CountVectorizer\n",
|
||||
"sc_vectorizer = CountVectorizer()\n",
|
||||
"corpus = [\n",
|
||||
" 'I like hot dogs.',\n",
|
||||
" 'The dog ran fast.',\n",
|
||||
" 'Its hot outside.',\n",
|
||||
" ]\n",
|
||||
"sc_vectorizer.fit_transform(corpus)\n",
|
||||
"sc_vectorizer.transform(['My dog likes hot dogs on a hot day.']).toarray()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"我們也可以使用我們在上面定義的 Keras 向量化器,將每個單詞編號轉換為一個獨熱編碼,然後將所有這些向量相加:\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 9,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"array([0., 5., 0., ..., 0., 0., 0.], dtype=float32)"
|
||||
]
|
||||
},
|
||||
"execution_count": 9,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"def to_bow(text):\n",
|
||||
" return tf.reduce_sum(tf.one_hot(vectorizer(text),vocab_size),axis=0)\n",
|
||||
"\n",
|
||||
"to_bow('My dog likes hot dogs on a hot day.').numpy()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"> **注意**:你可能會驚訝地發現結果與之前的例子有所不同。原因是,在 Keras 的例子中,向量的長度對應於詞彙表的大小,而這個詞彙表是基於整個 AG News 數據集建立的;而在 Scikit Learn 的例子中,我們是即時從樣本文本中建立詞彙表的。\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## 訓練 BoW 分類器\n",
|
||||
"\n",
|
||||
"現在我們已經學會如何建立文字的詞袋表示法,接下來讓我們訓練一個使用該表示法的分類器。首先,我們需要將數據集轉換為詞袋表示法。這可以通過以下方式使用 `map` 函數來實現:\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 11,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"batch_size = 128\n",
|
||||
"\n",
|
||||
"ds_train_bow = ds_train.map(lambda x: (to_bow(x['title']+x['description']),x['label'])).batch(batch_size)\n",
|
||||
"ds_test_bow = ds_test.map(lambda x: (to_bow(x['title']+x['description']),x['label'])).batch(batch_size)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"現在讓我們定義一個簡單的分類器神經網絡,其中包含一個線性層。輸入大小為 `vocab_size`,輸出大小對應於類別數量(4)。由於我們正在解決分類任務,最終的激活函數是 **softmax**:\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 12,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"938/938 [==============================] - 66s 70ms/step - loss: 0.6144 - acc: 0.8427 - val_loss: 0.4416 - val_acc: 0.8697\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"<keras.callbacks.History at 0x20c70a947f0>"
|
||||
]
|
||||
},
|
||||
"execution_count": 12,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"model = keras.models.Sequential([\n",
|
||||
" keras.layers.Dense(4,activation='softmax',input_shape=(vocab_size,))\n",
|
||||
"])\n",
|
||||
"model.compile(loss='sparse_categorical_crossentropy',optimizer='adam',metrics=['acc'])\n",
|
||||
"model.fit(ds_train_bow,validation_data=ds_test_bow)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"由於我們有四個類別,超過 80% 的準確率已經是一個不錯的結果。\n",
|
||||
"\n",
|
||||
"## 將分類器作為一個網絡進行訓練\n",
|
||||
"\n",
|
||||
"由於向量化器也是一個 Keras 層,我們可以定義一個包含它的網絡,並進行端到端的訓練。這樣我們就不需要使用 `map` 來向量化數據集,只需將原始數據集傳遞到網絡的輸入即可。\n",
|
||||
"\n",
|
||||
"> **注意**:我們仍然需要對數據集應用 map 操作,將字典中的字段(例如 `title`、`description` 和 `label`)轉換為元組。然而,當從磁碟加載數據時,我們可以一開始就構建具有所需結構的數據集。\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 13,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"Model: \"model\"\n",
|
||||
"_________________________________________________________________\n",
|
||||
" Layer (type) Output Shape Param # \n",
|
||||
"=================================================================\n",
|
||||
" input_1 (InputLayer) [(None, 1)] 0 \n",
|
||||
" \n",
|
||||
" text_vectorization (TextVec (None, None) 0 \n",
|
||||
" torization) \n",
|
||||
" \n",
|
||||
" tf.one_hot (TFOpLambda) (None, None, 5335) 0 \n",
|
||||
" \n",
|
||||
" tf.math.reduce_sum (TFOpLam (None, 5335) 0 \n",
|
||||
" bda) \n",
|
||||
" \n",
|
||||
" dense_2 (Dense) (None, 4) 21344 \n",
|
||||
" \n",
|
||||
"=================================================================\n",
|
||||
"Total params: 21,344\n",
|
||||
"Trainable params: 21,344\n",
|
||||
"Non-trainable params: 0\n",
|
||||
"_________________________________________________________________\n",
|
||||
"938/938 [==============================] - 73s 77ms/step - loss: 0.6057 - acc: 0.8414 - val_loss: 0.4202 - val_acc: 0.8736\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"<keras.callbacks.History at 0x20c721521f0>"
|
||||
]
|
||||
},
|
||||
"execution_count": 13,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"def extract_text(x):\n",
|
||||
" return x['title']+' '+x['description']\n",
|
||||
"\n",
|
||||
"def tupelize(x):\n",
|
||||
" return (extract_text(x),x['label'])\n",
|
||||
"\n",
|
||||
"inp = keras.Input(shape=(1,),dtype=tf.string)\n",
|
||||
"x = vectorizer(inp)\n",
|
||||
"x = tf.reduce_sum(tf.one_hot(x,vocab_size),axis=1)\n",
|
||||
"out = keras.layers.Dense(4,activation='softmax')(x)\n",
|
||||
"model = keras.models.Model(inp,out)\n",
|
||||
"model.summary()\n",
|
||||
"\n",
|
||||
"model.compile(loss='sparse_categorical_crossentropy',optimizer='adam',metrics=['acc'])\n",
|
||||
"model.fit(ds_train.map(tupelize).batch(batch_size),validation_data=ds_test.map(tupelize).batch(batch_size))\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## 雙字組、三字組和 n 字組\n",
|
||||
"\n",
|
||||
"袋裝詞語方法的一個限制是,有些詞語屬於多詞表達,例如「熱狗」這個詞的意思與「熱」和「狗」在其他語境中的意思完全不同。如果我們總是用相同的向量來表示「熱」和「狗」,可能會令模型感到混淆。\n",
|
||||
"\n",
|
||||
"為了解決這個問題,**n 字組表示法**經常用於文件分類的方法中,其中每個詞語、雙詞或三詞的頻率是訓練分類器的一個有用特徵。例如,在雙字組表示法中,我們會將所有詞語配對加入詞彙表,除了原本的詞語之外。\n",
|
||||
"\n",
|
||||
"以下是一個使用 Scikit Learn 生成雙字組袋裝詞語表示法的例子:\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 14,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"Vocabulary:\n",
|
||||
" {'i': 7, 'like': 11, 'hot': 4, 'dogs': 2, 'i like': 8, 'like hot': 12, 'hot dogs': 5, 'the': 16, 'dog': 0, 'ran': 14, 'fast': 3, 'the dog': 17, 'dog ran': 1, 'ran fast': 15, 'its': 9, 'outside': 13, 'its hot': 10, 'hot outside': 6}\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"array([[1, 0, 1, 0, 2, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]],\n",
|
||||
" dtype=int64)"
|
||||
]
|
||||
},
|
||||
"execution_count": 14,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"bigram_vectorizer = CountVectorizer(ngram_range=(1, 2), token_pattern=r'\\b\\w+\\b', min_df=1)\n",
|
||||
"corpus = [\n",
|
||||
" 'I like hot dogs.',\n",
|
||||
" 'The dog ran fast.',\n",
|
||||
" 'Its hot outside.',\n",
|
||||
" ]\n",
|
||||
"bigram_vectorizer.fit_transform(corpus)\n",
|
||||
"print(\"Vocabulary:\\n\",bigram_vectorizer.vocabulary_)\n",
|
||||
"bigram_vectorizer.transform(['My dog likes hot dogs on a hot day.']).toarray()\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"n-gram 方法的主要缺點是詞彙量會迅速膨脹。在實際應用中,我們需要將 n-gram 表示與降維技術結合使用,例如 *嵌入*,我們會在下一單元中討論。\n",
|
||||
"\n",
|
||||
"要在我們的 **AG News** 數據集中使用 n-gram 表示,我們需要將 `ngrams` 參數傳遞給 `TextVectorization` 建構函數。二元語法詞彙的長度**顯著更大**,在我們的情況下,超過 130 萬個詞元!因此,限制二元語法詞元的數量至合理範圍是有道理的。\n",
|
||||
"\n",
|
||||
"我們可以使用與上面相同的程式碼來訓練分類器,但這樣會非常耗費記憶體。在下一單元中,我們將使用嵌入來訓練二元語法分類器。同時,你可以在這個筆記本中嘗試訓練二元語法分類器,看看是否能獲得更高的準確率。\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## 自動計算 BoW 向量\n",
|
||||
"\n",
|
||||
"在上述例子中,我們透過手動方式計算 BoW 向量,方法是將個別詞語的一次性編碼相加。然而,最新版本的 TensorFlow 允許我們透過在向量化器構造函數中傳入 `output_mode='count` 參數,自動計算 BoW 向量。這使得定義和訓練模型變得更加簡單:\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 15,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"Training vectorizer\n",
|
||||
"938/938 [==============================] - 7s 7ms/step - loss: 0.5929 - acc: 0.8486 - val_loss: 0.4168 - val_acc: 0.8772\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"<keras.callbacks.History at 0x20c725217c0>"
|
||||
]
|
||||
},
|
||||
"execution_count": 15,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"model = keras.models.Sequential([\n",
|
||||
" keras.layers.experimental.preprocessing.TextVectorization(max_tokens=vocab_size,output_mode='count'),\n",
|
||||
" keras.layers.Dense(4,input_shape=(vocab_size,), activation='softmax')\n",
|
||||
"])\n",
|
||||
"print(\"Training vectorizer\")\n",
|
||||
"model.layers[0].adapt(ds_train.take(500).map(extract_text))\n",
|
||||
"model.compile(loss='sparse_categorical_crossentropy',optimizer='adam',metrics=['acc'])\n",
|
||||
"model.fit(ds_train.map(tupelize).batch(batch_size),validation_data=ds_test.map(tupelize).batch(batch_size))"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## 詞頻 - 逆文件頻率 (TF-IDF)\n",
|
||||
"\n",
|
||||
"在詞袋(BoW)表示法中,詞語的出現次數是使用相同的技術進行加權,而不考慮詞語本身。然而,很明顯像 *a* 和 *in* 這些常見詞語對分類的作用遠不如專業術語。在大多數自然語言處理(NLP)任務中,有些詞語比其他詞語更重要。\n",
|
||||
"\n",
|
||||
"**TF-IDF** 代表 **詞頻 - 逆文件頻率**。這是一種詞袋的變體,其中不是用二進制的 0/1 值來表示詞語是否出現在文件中,而是使用浮點值,該值與詞語在語料庫中的出現頻率相關。\n",
|
||||
"\n",
|
||||
"更正式地說,詞語 $i$ 在文件 $j$ 中的權重 $w_{ij}$ 定義為:\n",
|
||||
"$$\n",
|
||||
"w_{ij} = tf_{ij}\\times\\log({N\\over df_i})\n",
|
||||
"$$\n",
|
||||
"其中:\n",
|
||||
"* $tf_{ij}$ 是詞語 $i$ 在文件 $j$ 中的出現次數,即我們之前看到的詞袋值\n",
|
||||
"* $N$ 是集合中的文件數量\n",
|
||||
"* $df_i$ 是整個集合中包含詞語 $i$ 的文件數量\n",
|
||||
"\n",
|
||||
"TF-IDF 值 $w_{ij}$ 隨著詞語在文件中出現次數的增加而增加,同時會因語料庫中包含該詞語的文件數量而進行調整,這有助於平衡某些詞語出現頻率較高的情況。例如,如果某個詞語出現在集合中的 *每一個* 文件中,則 $df_i=N$,而 $w_{ij}=0$,這些詞語將被完全忽略。\n",
|
||||
"\n",
|
||||
"你可以使用 Scikit Learn 輕鬆地創建文本的 TF-IDF 向量化:\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 16,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"array([[0.43381609, 0. , 0.43381609, 0. , 0.65985664,\n",
|
||||
" 0.43381609, 0. , 0. , 0. , 0. ,\n",
|
||||
" 0. , 0. , 0. , 0. , 0. ,\n",
|
||||
" 0. ]])"
|
||||
]
|
||||
},
|
||||
"execution_count": 16,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"from sklearn.feature_extraction.text import TfidfVectorizer\n",
|
||||
"vectorizer = TfidfVectorizer(ngram_range=(1,2))\n",
|
||||
"vectorizer.fit_transform(corpus)\n",
|
||||
"vectorizer.transform(['My dog likes hot dogs on a hot day.']).toarray()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"在 Keras 中,`TextVectorization` 層可以通過傳遞 `output_mode='tf-idf'` 參數自動計算 TF-IDF 頻率。我們重複上面使用的代碼,看看使用 TF-IDF 是否能提高準確性:\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 17,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"Training vectorizer\n",
|
||||
"938/938 [==============================] - 12s 12ms/step - loss: 0.4197 - acc: 0.8662 - val_loss: 0.3432 - val_acc: 0.8849\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"<keras.callbacks.History at 0x20c729dfd30>"
|
||||
]
|
||||
},
|
||||
"execution_count": 17,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"model = keras.models.Sequential([\n",
|
||||
" keras.layers.experimental.preprocessing.TextVectorization(max_tokens=vocab_size,output_mode='tf-idf'),\n",
|
||||
" keras.layers.Dense(4,input_shape=(vocab_size,), activation='softmax')\n",
|
||||
"])\n",
|
||||
"print(\"Training vectorizer\")\n",
|
||||
"model.layers[0].adapt(ds_train.take(500).map(extract_text))\n",
|
||||
"model.compile(loss='sparse_categorical_crossentropy',optimizer='adam',metrics=['acc'])\n",
|
||||
"model.fit(ds_train.map(tupelize).batch(batch_size),validation_data=ds_test.map(tupelize).batch(batch_size))"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## 結論\n",
|
||||
"\n",
|
||||
"雖然 TF-IDF 表示法為不同的詞語提供了頻率權重,但它無法表達詞語的意義或順序。正如著名語言學家 J. R. Firth 在1935年所說:「詞語的完整意義總是與上下文相關,任何脫離上下文的意義研究都不應被認真對待。」我們稍後在課程中將學習如何通過語言建模來捕捉文本中的上下文信息。\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"\n---\n\n**免責聲明**: \n本文件已使用人工智能翻譯服務 [Co-op Translator](https://github.com/Azure/co-op-translator) 進行翻譯。雖然我們致力於提供準確的翻譯,但請注意,自動翻譯可能包含錯誤或不準確之處。原始語言的文件應被視為權威來源。對於重要資訊,建議使用專業人工翻譯。我們對因使用此翻譯而引起的任何誤解或錯誤解釋概不負責。\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"interpreter": {
|
||||
"hash": "0cb620c6d4b9f7a635928804c26cf22403d89d98d79684e4529119355ee6d5a5"
|
||||
},
|
||||
"kernel_info": {
|
||||
"name": "conda-env-py37_tensorflow-py"
|
||||
},
|
||||
"kernelspec": {
|
||||
"display_name": "py37_tensorflow",
|
||||
"language": "python",
|
||||
"name": "python3"
|
||||
},
|
||||
"language_info": {
|
||||
"codemirror_mode": {
|
||||
"name": "ipython",
|
||||
"version": 3
|
||||
},
|
||||
"file_extension": ".py",
|
||||
"mimetype": "text/x-python",
|
||||
"name": "python",
|
||||
"nbconvert_exporter": "python",
|
||||
"pygments_lexer": "ipython3",
|
||||
"version": "3.8.12"
|
||||
},
|
||||
"nteract": {
|
||||
"version": "nteract-front-end@1.0.0"
|
||||
},
|
||||
"coopTranslator": {
|
||||
"original_hash": "19b43951d55b377a76209c24c1f017e4",
|
||||
"translation_date": "2025-08-31T11:01:35+00:00",
|
||||
"source_file": "lessons/5-NLP/13-TextRep/TextRepresentationTF.ipynb",
|
||||
"language_code": "hk"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 4
|
||||
}
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
# 作業:筆記本
|
||||
|
||||
使用與本課程相關的筆記本(無論是 PyTorch 或 TensorFlow 版本),使用你自己的數據集重新執行,或許可以使用 Kaggle 上的數據集,並註明來源。重寫筆記本以強調你自己的發現。嘗試一些可能令人驚訝的創新數據集,例如 [這個關於 UFO 目擊事件的數據集](https://www.kaggle.com/datasets/NUFORC/ufo-sightings) 來自 NUFORC。
|
||||
|
||||
**免責聲明**:
|
||||
本文件已使用人工智能翻譯服務 [Co-op Translator](https://github.com/Azure/co-op-translator) 進行翻譯。雖然我們致力於提供準確的翻譯,但請注意,自動翻譯可能包含錯誤或不準確之處。原始語言的文件應被視為具權威性的來源。對於重要資訊,建議使用專業的人類翻譯服務。我們對因使用此翻譯而引起的任何誤解或錯誤解釋概不負責。
|
||||
|
|
@ -0,0 +1,724 @@
|
|||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## 嵌入\n",
|
||||
"\n",
|
||||
"在之前的例子中,我們使用了高維度的詞袋向量,其長度為 `vocab_size`,並且我們明確地將低維度的位置信息向量轉換為稀疏的獨熱表示。這種獨熱表示並不具備記憶效率,此外,每個詞都被獨立處理,也就是說,獨熱編碼的向量無法表達詞與詞之間的語義相似性。\n",
|
||||
"\n",
|
||||
"在這一單元中,我們將繼續探索 **News AG** 數據集。首先,讓我們載入數據並從之前的筆記本中獲取一些定義。\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 1,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"Loading dataset...\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "stderr",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"d:\\WORK\\ai-for-beginners\\5-NLP\\14-Embeddings\\data\\train.csv: 29.5MB [00:01, 18.8MB/s] \n",
|
||||
"d:\\WORK\\ai-for-beginners\\5-NLP\\14-Embeddings\\data\\test.csv: 1.86MB [00:00, 11.2MB/s] \n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"Building vocab...\n",
|
||||
"Vocab size = 95812\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"import torch\n",
|
||||
"import torchtext\n",
|
||||
"import numpy as np\n",
|
||||
"from torchnlp import *\n",
|
||||
"train_dataset, test_dataset, classes, vocab = load_dataset()\n",
|
||||
"vocab_size = len(vocab)\n",
|
||||
"print(\"Vocab size = \",vocab_size)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## 什麼是嵌入?\n",
|
||||
"\n",
|
||||
"**嵌入**的概念是用低維度的密集向量來表示單詞,這些向量能夠在某種程度上反映單詞的語義。我們稍後會討論如何構建有意義的單詞嵌入,但現在我們可以簡單地將嵌入理解為一種降低單詞向量維度的方法。\n",
|
||||
"\n",
|
||||
"因此,嵌入層會將一個單詞作為輸入,並生成一個指定 `embedding_size` 的輸出向量。從某種意義上說,它與 `Linear` 層非常相似,但不同的是,它不是接收 one-hot 編碼的向量,而是能夠接收一個單詞的編號作為輸入。\n",
|
||||
"\n",
|
||||
"通過將嵌入層作為我們網絡的第一層,我們可以從詞袋模型(bag-of-words)切換到 **嵌入袋模型**(embedding bag model)。在這個模型中,我們首先將文本中的每個單詞轉換為對應的嵌入,然後對所有這些嵌入執行某種聚合函數,例如 `sum`、`average` 或 `max`。\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"我們的分類器神經網絡將以嵌入層開始,接著是聚合層,最後在其上添加一個線性分類器:\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 2,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"class EmbedClassifier(torch.nn.Module):\n",
|
||||
" def __init__(self, vocab_size, embed_dim, num_class):\n",
|
||||
" super().__init__()\n",
|
||||
" self.embedding = torch.nn.Embedding(vocab_size, embed_dim)\n",
|
||||
" self.fc = torch.nn.Linear(embed_dim, num_class)\n",
|
||||
"\n",
|
||||
" def forward(self, x):\n",
|
||||
" x = self.embedding(x)\n",
|
||||
" x = torch.mean(x,dim=1)\n",
|
||||
" return self.fc(x)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"### 處理變化的序列大小\n",
|
||||
"\n",
|
||||
"由於這種架構,我們需要以特定方式建立送入網絡的迷你批次。在上一單元中,使用詞袋模型(bag-of-words)時,迷你批次中的所有 BoW 張量都具有相同的大小 `vocab_size`,無論文本序列的實際長度是多少。一旦我們轉向使用詞嵌入(word embeddings),每個文本樣本中的詞數會有所不同,而在將這些樣本合併成迷你批次時,我們需要進行一些填充。\n",
|
||||
"\n",
|
||||
"這可以通過向數據源提供 `collate_fn` 函數來完成:\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 3,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"def padify(b):\n",
|
||||
" # b is the list of tuples of length batch_size\n",
|
||||
" # - first element of a tuple = label, \n",
|
||||
" # - second = feature (text sequence)\n",
|
||||
" # build vectorized sequence\n",
|
||||
" v = [encode(x[1]) for x in b]\n",
|
||||
" # first, compute max length of a sequence in this minibatch\n",
|
||||
" l = max(map(len,v))\n",
|
||||
" return ( # tuple of two tensors - labels and features\n",
|
||||
" torch.LongTensor([t[0]-1 for t in b]),\n",
|
||||
" torch.stack([torch.nn.functional.pad(torch.tensor(t),(0,l-len(t)),mode='constant',value=0) for t in v])\n",
|
||||
" )\n",
|
||||
"\n",
|
||||
"train_loader = torch.utils.data.DataLoader(train_dataset, batch_size=16, collate_fn=padify, shuffle=True)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"### 訓練嵌入分類器\n",
|
||||
"\n",
|
||||
"現在我們已經定義了合適的數據加載器,我們可以使用上一單元中定義的訓練函數來訓練模型:\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 4,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"3200: acc=0.6415625\n",
|
||||
"6400: acc=0.6865625\n",
|
||||
"9600: acc=0.7103125\n",
|
||||
"12800: acc=0.726953125\n",
|
||||
"16000: acc=0.739375\n",
|
||||
"19200: acc=0.75046875\n",
|
||||
"22400: acc=0.7572321428571429\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"(0.889799795315499, 0.7623160588611644)"
|
||||
]
|
||||
},
|
||||
"execution_count": 4,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"net = EmbedClassifier(vocab_size,32,len(classes)).to(device)\n",
|
||||
"train_epoch(net,train_loader, lr=1, epoch_size=25000)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"> **注意**:我們在這裡僅訓練 25k 條記錄(少於一個完整的 epoch)以節省時間,但您可以繼續訓練,編寫一個函數來訓練多個 epoch,並嘗試調整學習率參數以獲得更高的準確率。您應該能夠達到約 90% 的準確率。\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"### EmbeddingBag 層與可變長度序列表示法\n",
|
||||
"\n",
|
||||
"在之前的架構中,我們需要將所有序列填充至相同的長度,以便將它們放入小批量中。這並不是表示可變長度序列的最有效方法——另一種方法是使用 **offset** 向量,該向量會保存所有序列在一個大型向量中的偏移量。\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"> **Note**: 在上圖中,我們展示的是字符序列,但在我們的例子中,我們處理的是單詞序列。然而,用偏移向量表示序列的基本原則仍然相同。\n",
|
||||
"\n",
|
||||
"為了使用偏移表示法,我們使用 [`EmbeddingBag`](https://pytorch.org/docs/stable/generated/torch.nn.EmbeddingBag.html) 層。它類似於 `Embedding`,但它以內容向量和偏移向量作為輸入,並且還包含一個平均層,可以是 `mean`、`sum` 或 `max`。\n",
|
||||
"\n",
|
||||
"以下是使用 `EmbeddingBag` 的修改後的網絡:\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 5,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"class EmbedClassifier(torch.nn.Module):\n",
|
||||
" def __init__(self, vocab_size, embed_dim, num_class):\n",
|
||||
" super().__init__()\n",
|
||||
" self.embedding = torch.nn.EmbeddingBag(vocab_size, embed_dim)\n",
|
||||
" self.fc = torch.nn.Linear(embed_dim, num_class)\n",
|
||||
"\n",
|
||||
" def forward(self, text, off):\n",
|
||||
" x = self.embedding(text, off)\n",
|
||||
" return self.fc(x)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"要準備數據集進行訓練,我們需要提供一個轉換函數來準備偏移向量:\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 6,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"def offsetify(b):\n",
|
||||
" # first, compute data tensor from all sequences\n",
|
||||
" x = [torch.tensor(encode(t[1])) for t in b]\n",
|
||||
" # now, compute the offsets by accumulating the tensor of sequence lengths\n",
|
||||
" o = [0] + [len(t) for t in x]\n",
|
||||
" o = torch.tensor(o[:-1]).cumsum(dim=0)\n",
|
||||
" return ( \n",
|
||||
" torch.LongTensor([t[0]-1 for t in b]), # labels\n",
|
||||
" torch.cat(x), # text \n",
|
||||
" o\n",
|
||||
" )\n",
|
||||
"\n",
|
||||
"train_loader = torch.utils.data.DataLoader(train_dataset, batch_size=16, collate_fn=offsetify, shuffle=True)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"注意,與之前的所有例子不同,我們的網絡現在接受兩個參數:數據向量和偏移向量,這兩者的大小不同。同樣,我們的數據加載器現在也提供了3個值而不是2個:文本和偏移向量都作為特徵提供。因此,我們需要稍微調整我們的訓練函數來處理這一點:\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 7,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"3200: acc=0.6153125\n",
|
||||
"6400: acc=0.6615625\n",
|
||||
"9600: acc=0.6932291666666667\n",
|
||||
"12800: acc=0.715078125\n",
|
||||
"16000: acc=0.7270625\n",
|
||||
"19200: acc=0.7382291666666667\n",
|
||||
"22400: acc=0.7486160714285715\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"(22.771553103007037, 0.7551983365323096)"
|
||||
]
|
||||
},
|
||||
"execution_count": 7,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"net = EmbedClassifier(vocab_size,32,len(classes)).to(device)\n",
|
||||
"\n",
|
||||
"def train_epoch_emb(net,dataloader,lr=0.01,optimizer=None,loss_fn = torch.nn.CrossEntropyLoss(),epoch_size=None, report_freq=200):\n",
|
||||
" optimizer = optimizer or torch.optim.Adam(net.parameters(),lr=lr)\n",
|
||||
" loss_fn = loss_fn.to(device)\n",
|
||||
" net.train()\n",
|
||||
" total_loss,acc,count,i = 0,0,0,0\n",
|
||||
" for labels,text,off in dataloader:\n",
|
||||
" optimizer.zero_grad()\n",
|
||||
" labels,text,off = labels.to(device), text.to(device), off.to(device)\n",
|
||||
" out = net(text, off)\n",
|
||||
" loss = loss_fn(out,labels) #cross_entropy(out,labels)\n",
|
||||
" loss.backward()\n",
|
||||
" optimizer.step()\n",
|
||||
" total_loss+=loss\n",
|
||||
" _,predicted = torch.max(out,1)\n",
|
||||
" acc+=(predicted==labels).sum()\n",
|
||||
" count+=len(labels)\n",
|
||||
" i+=1\n",
|
||||
" if i%report_freq==0:\n",
|
||||
" print(f\"{count}: acc={acc.item()/count}\")\n",
|
||||
" if epoch_size and count>epoch_size:\n",
|
||||
" break\n",
|
||||
" return total_loss.item()/count, acc.item()/count\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"train_epoch_emb(net,train_loader, lr=4, epoch_size=25000)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## 語意嵌入:Word2Vec\n",
|
||||
"\n",
|
||||
"在我們之前的例子中,模型的嵌入層學會了將單詞映射到向量表示,但這種表示並沒有太多語意上的意義。如果能學習到這樣的向量表示,讓相似的單詞或同義詞在某種向量距離(例如歐幾里得距離)上彼此接近,那就更理想了。\n",
|
||||
"\n",
|
||||
"為了實現這一點,我們需要以特定的方式在大量文本數據上預訓練嵌入模型。最早的語意嵌入訓練方法之一被稱為 [Word2Vec](https://en.wikipedia.org/wiki/Word2vec)。它基於兩種主要架構,用於生成單詞的分佈式表示:\n",
|
||||
"\n",
|
||||
" - **連續詞袋模型** (CBoW) — 在這種架構中,我們訓練模型根據周圍的上下文來預測一個單詞。給定 ngram $(W_{-2},W_{-1},W_0,W_1,W_2)$,模型的目標是從 $(W_{-2},W_{-1},W_1,W_2)$ 預測 $W_0$。\n",
|
||||
" - **連續跳字模型** (Skip-Gram) 與 CBoW 相反。模型使用周圍的上下文窗口單詞來預測當前單詞。\n",
|
||||
"\n",
|
||||
"CBoW 的速度較快,而 Skip-Gram 雖然較慢,但在表示不常見單詞方面表現更好。\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"為了試驗在 Google News 數據集上預訓練的 Word2Vec 嵌入,我們可以使用 **gensim** 庫。以下是找到與 'neural' 最相似的單詞的例子:\n",
|
||||
"\n",
|
||||
"> **注意:** 當你第一次創建單詞向量時,下載它們可能需要一些時間!\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 8,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import gensim.downloader as api\n",
|
||||
"w2v = api.load('word2vec-google-news-300')"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 9,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"neuronal -> 0.7804799675941467\n",
|
||||
"neurons -> 0.7326500415802002\n",
|
||||
"neural_circuits -> 0.7252851724624634\n",
|
||||
"neuron -> 0.7174385190010071\n",
|
||||
"cortical -> 0.6941086649894714\n",
|
||||
"brain_circuitry -> 0.6923246383666992\n",
|
||||
"synaptic -> 0.6699118614196777\n",
|
||||
"neural_circuitry -> 0.6638563275337219\n",
|
||||
"neurochemical -> 0.6555314064025879\n",
|
||||
"neuronal_activity -> 0.6531826257705688\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"for w,p in w2v.most_similar('neural'):\n",
|
||||
" print(f\"{w} -> {p}\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"我們亦可從該詞計算向量嵌入,用於訓練分類模型(為清晰起見,我們僅顯示向量的前20個組件):\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 10,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"array([ 0.01226807, 0.06225586, 0.10693359, 0.05810547, 0.23828125,\n",
|
||||
" 0.03686523, 0.05151367, -0.20703125, 0.01989746, 0.10058594,\n",
|
||||
" -0.03759766, -0.1015625 , -0.15820312, -0.08105469, -0.0390625 ,\n",
|
||||
" -0.05053711, 0.16015625, 0.2578125 , 0.10058594, -0.25976562],\n",
|
||||
" dtype=float32)"
|
||||
]
|
||||
},
|
||||
"execution_count": 10,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"w2v.word_vec('play')[:20]"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"語義嵌入的優點在於你可以操作向量編碼來改變語義。例如,我們可以要求找到一個詞,其向量表示盡可能接近詞語*國王*和*女人*,並且盡可能遠離詞語*男人*:\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 10,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"('queen', 0.7118192911148071)"
|
||||
]
|
||||
},
|
||||
"execution_count": 10,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"w2v.most_similar(positive=['king','woman'],negative=['man'])[0]"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"CBoW 和 Skip-Grams 都是「預測型」的詞嵌入方法,因為它們只考慮局部上下文。Word2Vec 並沒有利用全局上下文。\n",
|
||||
"\n",
|
||||
"**FastText** 在 Word2Vec 的基礎上進一步發展,通過學習每個詞的向量表示以及詞內的字符 n-grams。這些表示的值在每次訓練步驟中會被平均成一個向量。雖然這增加了預訓練的計算量,但它使詞嵌入能夠編碼子詞資訊。\n",
|
||||
"\n",
|
||||
"另一種方法,**GloVe**,利用了共現矩陣的概念,通過神經方法將共現矩陣分解為更具表達力和非線性的詞向量。\n",
|
||||
"\n",
|
||||
"你可以通過將嵌入模型切換為 FastText 和 GloVe 來試驗這些例子,因為 gensim 支援多種不同的詞嵌入模型。\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## 在 PyTorch 中使用預訓練的嵌入\n",
|
||||
"\n",
|
||||
"我們可以修改上述範例,將嵌入層中的矩陣預先填入語義嵌入,例如 Word2Vec。我們需要考慮到,預訓練嵌入的詞彙表與我們文本語料庫的詞彙表可能不一致,因此我們會用隨機值初始化缺失詞彙的權重:\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 11,
|
||||
"metadata": {
|
||||
"tags": []
|
||||
},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"Embedding size: 300\n",
|
||||
"Populating matrix, this will take some time...Done, found 41080 words, 54732 words missing\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"embed_size = len(w2v.get_vector('hello'))\n",
|
||||
"print(f'Embedding size: {embed_size}')\n",
|
||||
"\n",
|
||||
"net = EmbedClassifier(vocab_size,embed_size,len(classes))\n",
|
||||
"\n",
|
||||
"print('Populating matrix, this will take some time...',end='')\n",
|
||||
"found, not_found = 0,0\n",
|
||||
"for i,w in enumerate(vocab.get_itos()):\n",
|
||||
" try:\n",
|
||||
" net.embedding.weight[i].data = torch.tensor(w2v.get_vector(w))\n",
|
||||
" found+=1\n",
|
||||
" except:\n",
|
||||
" net.embedding.weight[i].data = torch.normal(0.0,1.0,(embed_size,))\n",
|
||||
" not_found+=1\n",
|
||||
"\n",
|
||||
"print(f\"Done, found {found} words, {not_found} words missing\")\n",
|
||||
"net = net.to(device)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"現在讓我們訓練模型。請注意,由於嵌入層的大小更大,因此參數的數量也大幅增加,訓練模型所需的時間比之前的例子顯著增加。此外,正因如此,如果我們想避免過度擬合,可能需要在更多的例子上訓練模型。\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 12,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"3200: acc=0.6359375\n",
|
||||
"6400: acc=0.68109375\n",
|
||||
"9600: acc=0.7067708333333333\n",
|
||||
"12800: acc=0.723671875\n",
|
||||
"16000: acc=0.73625\n",
|
||||
"19200: acc=0.7463541666666667\n",
|
||||
"22400: acc=0.7560714285714286\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"(214.1013875559821, 0.7626759436980166)"
|
||||
]
|
||||
},
|
||||
"execution_count": 12,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"train_epoch_emb(net,train_loader, lr=4, epoch_size=25000)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"在我們的情況中,準確度並沒有顯著提升,這可能是由於詞彙差異較大所致。 \n",
|
||||
"為了解決詞彙差異的問題,我們可以採用以下其中一種解決方案: \n",
|
||||
"* 重新訓練 word2vec 模型,使用我們的詞彙 \n",
|
||||
"* 使用預訓練的 word2vec 模型的詞彙來載入我們的數據集。在載入數據集時,可以指定使用的詞彙。 \n",
|
||||
"\n",
|
||||
"後者的方法似乎更簡單,特別是因為 PyTorch 的 `torchtext` 框架內建了對嵌入的支持。例如,我們可以以下列方式實例化基於 GloVe 的詞彙: \n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 14,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stderr",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"100%|█████████▉| 399999/400000 [00:15<00:00, 25411.14it/s]\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"vocab = torchtext.vocab.GloVe(name='6B', dim=50)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"已載入的詞彙具有以下基本操作:\n",
|
||||
"* `vocab.stoi` 字典可讓我們將單詞轉換為其字典索引\n",
|
||||
"* `vocab.itos` 則相反 - 將數字轉換為單詞\n",
|
||||
"* `vocab.vectors` 是嵌入向量的陣列,因此要獲取單詞 `s` 的嵌入,我們需要使用 `vocab.vectors[vocab.stoi[s]]`\n",
|
||||
"\n",
|
||||
"以下是一個操作嵌入的例子,用以展示方程式 **kind-man+woman = queen**(我稍微調整了一下係數以使其生效):\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 15,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"'queen'"
|
||||
]
|
||||
},
|
||||
"execution_count": 15,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"# get the vector corresponding to kind-man+woman\n",
|
||||
"qvec = vocab.vectors[vocab.stoi['king']]-vocab.vectors[vocab.stoi['man']]+1.3*vocab.vectors[vocab.stoi['woman']]\n",
|
||||
"# find the index of the closest embedding vector \n",
|
||||
"d = torch.sum((vocab.vectors-qvec)**2,dim=1)\n",
|
||||
"min_idx = torch.argmin(d)\n",
|
||||
"# find the corresponding word\n",
|
||||
"vocab.itos[min_idx]"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"要使用這些嵌入來訓練分類器,我們首先需要使用GloVe詞彙表對我們的數據集進行編碼:\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 16,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"def offsetify(b):\n",
|
||||
" # first, compute data tensor from all sequences\n",
|
||||
" x = [torch.tensor(encode(t[1],voc=vocab)) for t in b] # pass the instance of vocab to encode function!\n",
|
||||
" # now, compute the offsets by accumulating the tensor of sequence lengths\n",
|
||||
" o = [0] + [len(t) for t in x]\n",
|
||||
" o = torch.tensor(o[:-1]).cumsum(dim=0)\n",
|
||||
" return ( \n",
|
||||
" torch.LongTensor([t[0]-1 for t in b]), # labels\n",
|
||||
" torch.cat(x), # text \n",
|
||||
" o\n",
|
||||
" )"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"正如我們上面所見,所有向量嵌入都存儲在 `vocab.vectors` 矩陣中。這使得通過簡單的複製將這些權重加載到嵌入層的權重中變得非常容易:\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 17,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"net = EmbedClassifier(len(vocab),len(vocab.vectors[0]),len(classes))\n",
|
||||
"net.embedding.weight.data = vocab.vectors\n",
|
||||
"net = net.to(device)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": []
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 18,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"3200: acc=0.6271875\n",
|
||||
"6400: acc=0.68078125\n",
|
||||
"9600: acc=0.7030208333333333\n",
|
||||
"12800: acc=0.71984375\n",
|
||||
"16000: acc=0.7346875\n",
|
||||
"19200: acc=0.7455729166666667\n",
|
||||
"22400: acc=0.7529464285714286\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"(35.53972978646833, 0.7575175943698017)"
|
||||
]
|
||||
},
|
||||
"execution_count": 18,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"train_loader = torch.utils.data.DataLoader(train_dataset, batch_size=16, collate_fn=offsetify, shuffle=True)\n",
|
||||
"train_epoch_emb(net,train_loader, lr=4, epoch_size=25000)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"我們未能看到準確性顯著提高的其中一個原因是因為我們的數據集中的某些詞語在預訓練的GloVe詞彙表中缺失,因此它們基本上被忽略了。為了克服這一問題,我們可以在我們的數據集上訓練自己的嵌入。\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## 語境嵌入\n",
|
||||
"\n",
|
||||
"傳統預訓練嵌入表示(例如 Word2Vec)的一個主要限制是詞義消歧的問題。雖然預訓練嵌入可以捕捉到一些詞語在語境中的含義,但每個詞的所有可能含義都被編碼到同一個嵌入中。這可能會在下游模型中引發問題,因為許多詞語(例如 \"play\")的含義會根據使用的語境而有所不同。\n",
|
||||
"\n",
|
||||
"例如,\"play\" 在以下兩個句子中的含義就完全不同:\n",
|
||||
"- 我去劇院看了一場**戲劇**。\n",
|
||||
"- 約翰想和他的朋友**玩耍**。\n",
|
||||
"\n",
|
||||
"上述的預訓練嵌入將 \"play\" 的這兩種含義表示為同一個嵌入。為了解決這個限制,我們需要基於**語言模型**來構建嵌入,語言模型是在大量文本語料庫上訓練的,並且*了解*詞語如何在不同語境中組合使用。討論語境嵌入超出了本教程的範圍,但我們會在下一單元討論語言模型時回到這個主題。\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"\n---\n\n**免責聲明**: \n本文件已使用人工智能翻譯服務 [Co-op Translator](https://github.com/Azure/co-op-translator) 進行翻譯。我們致力於提供準確的翻譯,但請注意,自動翻譯可能包含錯誤或不準確之處。應以原文文件作為權威來源。對於關鍵資訊,建議尋求專業人工翻譯。我們對因使用此翻譯而引起的任何誤解或誤釋不承擔責任。\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"interpreter": {
|
||||
"hash": "0cb620c6d4b9f7a635928804c26cf22403d89d98d79684e4529119355ee6d5a5"
|
||||
},
|
||||
"kernelspec": {
|
||||
"display_name": "py37_pytorch",
|
||||
"language": "python",
|
||||
"name": "python3"
|
||||
},
|
||||
"language_info": {
|
||||
"codemirror_mode": {
|
||||
"name": "ipython",
|
||||
"version": 3
|
||||
},
|
||||
"file_extension": ".py",
|
||||
"mimetype": "text/x-python",
|
||||
"name": "python",
|
||||
"nbconvert_exporter": "python",
|
||||
"pygments_lexer": "ipython3",
|
||||
"version": "3.8.12"
|
||||
},
|
||||
"coopTranslator": {
|
||||
"original_hash": "f50b026abce5cf36783a560ea72cb9b1",
|
||||
"translation_date": "2025-08-31T10:56:44+00:00",
|
||||
"source_file": "lessons/5-NLP/14-Embeddings/EmbeddingsPyTorch.ipynb",
|
||||
"language_code": "hk"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 2
|
||||
}
|
||||
|
|
@ -0,0 +1,695 @@
|
|||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## 嵌入\n",
|
||||
"\n",
|
||||
"在我們之前的例子中,我們操作的是長度為 `vocab_size` 的高維度詞袋向量,並且我們將低維度的位置表示向量顯式地轉換為稀疏的獨熱表示(one-hot representation)。這種獨熱表示並不具備記憶效率。此外,每個詞彙都被獨立處理,因此獨熱編碼向量無法表達詞彙之間的語義相似性。\n",
|
||||
"\n",
|
||||
"在本單元中,我們將繼續探索 **News AG** 數據集。首先,讓我們載入數據並從上一單元中獲取一些定義。\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 2,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import tensorflow as tf\n",
|
||||
"from tensorflow import keras\n",
|
||||
"import tensorflow_datasets as tfds\n",
|
||||
"import numpy as np\n",
|
||||
"\n",
|
||||
"ds_train, ds_test = tfds.load('ag_news_subset').values()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"### 什麼是嵌入?\n",
|
||||
"\n",
|
||||
"**嵌入**的概念是用低維度的密集向量來表示詞語,這些向量反映了詞語的語義。稍後我們會討論如何構建有意義的詞嵌入,但現在可以簡單地將嵌入理解為一種降低詞向量維度的方法。\n",
|
||||
"\n",
|
||||
"嵌入層以詞語作為輸入,並生成指定的 `embedding_size` 的輸出向量。某種程度上,它與 `Dense` 層非常相似,但嵌入層不需要以 one-hot 編碼向量作為輸入,而是可以直接接受詞語編號。\n",
|
||||
"\n",
|
||||
"通過在網絡中使用嵌入層作為第一層,我們可以從詞袋模型切換到 **嵌入袋** 模型。在嵌入袋模型中,我們首先將文本中的每個詞轉換為相應的嵌入,然後對所有嵌入進行某種聚合函數計算,例如 `sum`、`average` 或 `max`。\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"我們的分類器神經網絡包含以下幾層:\n",
|
||||
"\n",
|
||||
"* `TextVectorization` 層:該層以字符串作為輸入,並生成包含詞語編號的張量。我們會指定一個合理的詞彙表大小 `vocab_size`,並忽略使用頻率較低的詞語。輸入形狀為 1,輸出形狀為 $n$,因為結果中會有 $n$ 個標記,每個標記包含從 0 到 `vocab_size` 的數字。\n",
|
||||
"* `Embedding` 層:該層接收 $n$ 個數字,並將每個數字縮減為指定長度的密集向量(在我們的例子中是 100)。因此,形狀為 $n$ 的輸入張量將被轉換為 $n\\times 100$ 的張量。\n",
|
||||
"* 聚合層:該層沿第一軸計算該張量的平均值,即計算所有 $n$ 個輸入張量(對應不同詞語)的平均值。為了實現該層,我們會使用 `Lambda` 層,並傳入計算平均值的函數。輸出形狀為 100,這將是整個輸入序列的數字表示。\n",
|
||||
"* 最後的 `Dense` 線性分類器。\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 3,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"Model: \"sequential\"\n",
|
||||
"_________________________________________________________________\n",
|
||||
" Layer (type) Output Shape Param # \n",
|
||||
"=================================================================\n",
|
||||
" text_vectorization (TextVec (None, None) 0 \n",
|
||||
" torization) \n",
|
||||
" \n",
|
||||
" embedding (Embedding) (None, None, 100) 3000000 \n",
|
||||
" \n",
|
||||
" lambda (Lambda) (None, 100) 0 \n",
|
||||
" \n",
|
||||
" dense (Dense) (None, 4) 404 \n",
|
||||
" \n",
|
||||
"=================================================================\n",
|
||||
"Total params: 3,000,404\n",
|
||||
"Trainable params: 3,000,404\n",
|
||||
"Non-trainable params: 0\n",
|
||||
"_________________________________________________________________\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"vocab_size = 30000\n",
|
||||
"batch_size = 128\n",
|
||||
"\n",
|
||||
"vectorizer = keras.layers.experimental.preprocessing.TextVectorization(max_tokens=vocab_size,input_shape=(1,))\n",
|
||||
"\n",
|
||||
"model = keras.models.Sequential([\n",
|
||||
" vectorizer, \n",
|
||||
" keras.layers.Embedding(vocab_size,100),\n",
|
||||
" keras.layers.Lambda(lambda x: tf.reduce_mean(x,axis=1)),\n",
|
||||
" keras.layers.Dense(4, activation='softmax')\n",
|
||||
"])\n",
|
||||
"model.summary()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"在 `summary` 的輸出中,**output shape** 欄位中的第一個張量維度 `None` 代表小批量的大小,第二個維度則代表標記序列的長度。小批量中的所有標記序列長度都不相同。我們會在下一節討論如何處理這個問題。\n",
|
||||
"\n",
|
||||
"現在讓我們開始訓練網絡:\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 4,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"Training vectorizer\n",
|
||||
"938/938 [==============================] - 20s 20ms/step - loss: 0.7891 - acc: 0.8155 - val_loss: 0.4470 - val_acc: 0.8642\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"<keras.callbacks.History at 0x22255515100>"
|
||||
]
|
||||
},
|
||||
"execution_count": 4,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"def extract_text(x):\n",
|
||||
" return x['title']+' '+x['description']\n",
|
||||
"\n",
|
||||
"def tupelize(x):\n",
|
||||
" return (extract_text(x),x['label'])\n",
|
||||
"\n",
|
||||
"print(\"Training vectorizer\")\n",
|
||||
"vectorizer.adapt(ds_train.take(500).map(extract_text))\n",
|
||||
"\n",
|
||||
"model.compile(loss='sparse_categorical_crossentropy',metrics=['acc'])\n",
|
||||
"model.fit(ds_train.map(tupelize).batch(batch_size),validation_data=ds_test.map(tupelize).batch(batch_size))"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"nteract": {
|
||||
"transient": {
|
||||
"deleting": false
|
||||
}
|
||||
}
|
||||
},
|
||||
"source": [
|
||||
"> **注意** 我們正在基於數據的一個子集構建向量化器。這樣做是為了加快過程,但可能會導致我們文本中的某些詞彙未出現在詞彙表中。在這種情況下,這些詞彙將被忽略,這可能會導致準確性略有下降。然而,在現實生活中,文本的子集通常能提供良好的詞彙估計。\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"### 處理變量序列長度\n",
|
||||
"\n",
|
||||
"讓我們了解小批量訓練的過程。在上述例子中,輸入張量的維度是1,我們使用長度為128的小批量,因此張量的實際大小是 $128 \\times 1$。然而,每個句子的詞元數量是不同的。如果我們將 `TextVectorization` 層應用到單一輸入,返回的詞元數量會根據文本如何被分詞而有所不同:\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 5,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"tf.Tensor([ 1 45], shape=(2,), dtype=int64)\n",
|
||||
"tf.Tensor([ 112 1271 1 3 1747 158], shape=(6,), dtype=int64)\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"print(vectorizer('Hello, world!'))\n",
|
||||
"print(vectorizer('I am glad to meet you!'))"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"然而,當我們將向量化器應用於多個序列時,它必須生成一個矩形形狀的張量,因此會用 PAD 標記(在我們的情況下是零)填充未使用的元素:\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 6,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"<tf.Tensor: shape=(2, 6), dtype=int64, numpy=\n",
|
||||
"array([[ 1, 45, 0, 0, 0, 0],\n",
|
||||
" [ 112, 1271, 1, 3, 1747, 158]], dtype=int64)>"
|
||||
]
|
||||
},
|
||||
"execution_count": 6,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"vectorizer(['Hello, world!','I am glad to meet you!'])"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"在這裡我們可以看到嵌入:\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 7,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"array([[[ 1.53059261e-02, 6.80514947e-02, 3.14026810e-02, ...,\n",
|
||||
" -8.92002955e-02, 1.52911525e-04, -5.65562584e-02],\n",
|
||||
" [ 2.57456154e-01, 2.79364467e-01, -2.03605562e-01, ...,\n",
|
||||
" -2.07474351e-01, 8.31158683e-02, -2.03911960e-01],\n",
|
||||
" [ 3.98201384e-02, -8.03454965e-03, 2.39790026e-02, ...,\n",
|
||||
" -7.18549127e-04, 2.66963355e-02, -4.30646613e-02],\n",
|
||||
" [ 3.98201384e-02, -8.03454965e-03, 2.39790026e-02, ...,\n",
|
||||
" -7.18549127e-04, 2.66963355e-02, -4.30646613e-02],\n",
|
||||
" [ 3.98201384e-02, -8.03454965e-03, 2.39790026e-02, ...,\n",
|
||||
" -7.18549127e-04, 2.66963355e-02, -4.30646613e-02],\n",
|
||||
" [ 3.98201384e-02, -8.03454965e-03, 2.39790026e-02, ...,\n",
|
||||
" -7.18549127e-04, 2.66963355e-02, -4.30646613e-02]],\n",
|
||||
"\n",
|
||||
" [[ 1.89674050e-01, 2.61548996e-01, -3.67433839e-02, ...,\n",
|
||||
" -2.07366899e-01, -1.05442435e-01, -2.36952081e-01],\n",
|
||||
" [ 6.16133213e-02, 1.80511594e-01, 9.77298319e-02, ...,\n",
|
||||
" -5.46628237e-02, -1.07340455e-01, -1.06589928e-01],\n",
|
||||
" [ 1.53059261e-02, 6.80514947e-02, 3.14026810e-02, ...,\n",
|
||||
" -8.92002955e-02, 1.52911525e-04, -5.65562584e-02],\n",
|
||||
" [-4.84890305e-02, -8.41715634e-02, 1.51529670e-01, ...,\n",
|
||||
" 1.28192469e-01, -7.77286515e-02, 1.26041949e-01],\n",
|
||||
" [-4.17212099e-02, -5.60694858e-02, 4.08860669e-02, ...,\n",
|
||||
" 8.70475471e-02, 8.92383084e-02, 1.67974353e-01],\n",
|
||||
" [ 2.85779923e-01, 4.57767487e-01, 4.52292450e-02, ...,\n",
|
||||
" -1.97419018e-01, -2.04659685e-01, -2.79758364e-01]]],\n",
|
||||
" dtype=float32)"
|
||||
]
|
||||
},
|
||||
"execution_count": 7,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"model.layers[1](vectorizer(['Hello, world!','I am glad to meet you!'])).numpy()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"> **注意**:為了減少填充的數量,在某些情況下,將數據集中所有序列按長度(更準確地說,是按標記數量)遞增的順序排序是有意義的。這將確保每個小批次包含長度相似的序列。\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## 語義嵌入:Word2Vec\n",
|
||||
"\n",
|
||||
"在之前的例子中,嵌入層學習如何將文字映射到向量表示,但這些表示並不具有語義上的意義。如果能學習一種向量表示,使得相似的詞或同義詞在某種向量距離(例如歐幾里得距離)上彼此接近,那就更好了。\n",
|
||||
"\n",
|
||||
"為了達到這個目的,我們需要使用像 [Word2Vec](https://en.wikipedia.org/wiki/Word2vec) 這樣的技術,在大量文本集合上預訓練嵌入模型。Word2Vec 基於兩種主要架構,用於生成詞的分佈式表示:\n",
|
||||
"\n",
|
||||
" - **連續詞袋模型** (CBoW),我們訓練模型根據周圍的上下文來預測一個詞。給定 ngram $(W_{-2},W_{-1},W_0,W_1,W_2)$,模型的目標是根據 $(W_{-2},W_{-1},W_1,W_2)$ 預測 $W_0$。\n",
|
||||
" - **連續跳字模型** (Skip-Gram) 與 CBoW 相反。模型使用周圍的上下文詞窗口來預測當前的詞。\n",
|
||||
"\n",
|
||||
"CBoW 的速度較快,而 Skip-Gram 雖然較慢,但在表示不常見的詞方面效果更好。\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"為了試驗基於 Google News 數據集預訓練的 Word2Vec 嵌入,我們可以使用 **gensim** 庫。以下是找到與「neural」最相似的詞的示例。\n",
|
||||
"\n",
|
||||
"> **注意:** 當你第一次創建詞向量時,下載它們可能需要一些時間!\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 8,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import gensim.downloader as api\n",
|
||||
"w2v = api.load('word2vec-google-news-300')"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 12,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"neuronal -> 0.7804799675941467\n",
|
||||
"neurons -> 0.7326500415802002\n",
|
||||
"neural_circuits -> 0.7252851724624634\n",
|
||||
"neuron -> 0.7174385190010071\n",
|
||||
"cortical -> 0.6941086649894714\n",
|
||||
"brain_circuitry -> 0.6923246383666992\n",
|
||||
"synaptic -> 0.6699118614196777\n",
|
||||
"neural_circuitry -> 0.6638563275337219\n",
|
||||
"neurochemical -> 0.6555314064025879\n",
|
||||
"neuronal_activity -> 0.6531826257705688\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"for w,p in w2v.most_similar('neural'):\n",
|
||||
" print(f\"{w} -> {p}\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"我們還可以從該詞中提取向量嵌入,用於訓練分類模型。該嵌入有300個組件,但這裡為了清晰起見,我們僅顯示向量的前20個組件:\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 13,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"array([ 0.01226807, 0.06225586, 0.10693359, 0.05810547, 0.23828125,\n",
|
||||
" 0.03686523, 0.05151367, -0.20703125, 0.01989746, 0.10058594,\n",
|
||||
" -0.03759766, -0.1015625 , -0.15820312, -0.08105469, -0.0390625 ,\n",
|
||||
" -0.05053711, 0.16015625, 0.2578125 , 0.10058594, -0.25976562],\n",
|
||||
" dtype=float32)"
|
||||
]
|
||||
},
|
||||
"execution_count": 13,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"w2v['play'][:20]"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"語義嵌入的偉大之處在於你可以根據語義操控向量編碼。例如,我們可以要求找到一個詞,其向量表示盡可能接近詞語*國王*和*女人*,並且盡可能遠離詞語*男人*:\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 14,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"('queen', 0.7118192911148071)"
|
||||
]
|
||||
},
|
||||
"execution_count": 14,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"w2v.most_similar(positive=['king','woman'],negative=['man'])[0]"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"tags": []
|
||||
},
|
||||
"source": [
|
||||
"上面的例子使用了一些內部的 GenSym 魔法,但其基本邏輯其實相當簡單。關於嵌入的一個有趣之處是,你可以對嵌入向量執行正常的向量運算,而這將反映在詞語**意義**上的操作。上面的例子可以用向量運算來表達:我們計算出對應於 **KING-MAN+WOMAN** 的向量(對應詞語的向量表示執行 `+` 和 `-` 操作),然後在字典中找到最接近該向量的詞語:\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 15,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"'queen'"
|
||||
]
|
||||
},
|
||||
"execution_count": 15,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"# get the vector corresponding to kind-man+woman\n",
|
||||
"qvec = w2v['king']-1.7*w2v['man']+1.7*w2v['woman']\n",
|
||||
"# find the index of the closest embedding vector \n",
|
||||
"d = np.sum((w2v.vectors-qvec)**2,axis=1)\n",
|
||||
"min_idx = np.argmin(d)\n",
|
||||
"# find the corresponding word\n",
|
||||
"w2v.index_to_key[min_idx]"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"> **注意**:我們需要在 *man* 和 *woman* 向量中加入一個小係數——試試移除它們,看看會發生什麼。\n",
|
||||
"\n",
|
||||
"為了找到最接近的向量,我們使用 TensorFlow 的工具來計算我們的向量與詞彙表中所有向量之間的距離向量,然後使用 `argmin` 找出最小距離的詞的索引。\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"雖然 Word2Vec 是一種表達詞語語義的好方法,但它有許多缺點,包括以下幾點:\n",
|
||||
"\n",
|
||||
"* CBoW 和 skip-gram 模型都是**預測型嵌入**,它們只考慮局部上下文。Word2Vec 無法利用全局上下文。\n",
|
||||
"* Word2Vec 沒有考慮到詞語的**形態學**,即詞語的意義可能取決於詞的不同部分,例如詞根。\n",
|
||||
"\n",
|
||||
"**FastText** 嘗試克服第二個限制,並在 Word2Vec 的基礎上進一步改進,通過學習每個詞的向量表示以及詞內的字符 n-gram。這些表示的值在每次訓練步驟中會被平均成一個向量。雖然這為預訓練增加了大量額外的計算,但它使詞嵌入能夠編碼子詞資訊。\n",
|
||||
"\n",
|
||||
"另一種方法,**GloVe**,採用了與 Word2Vec 不同的詞嵌入方法,基於詞-上下文矩陣的分解。首先,它構建了一個大型矩陣,記錄詞語在不同上下文中的出現次數,然後嘗試以降低維度的方式表示該矩陣,同時最小化重建損失。\n",
|
||||
"\n",
|
||||
"gensim 庫支持這些詞嵌入方法,您可以通過修改上方的模型加載代碼來嘗試使用它們。\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## 在 Keras 中使用預訓練的嵌入\n",
|
||||
"\n",
|
||||
"我們可以修改上述範例,在嵌入層的矩陣中預先填入語義嵌入,例如 Word2Vec。預訓練嵌入的詞彙表和文本語料庫的詞彙表可能不一致,因此我們需要選擇其中一個。在這裡,我們探討兩種可能的選擇:使用分詞器的詞彙表,或者使用 Word2Vec 嵌入的詞彙表。\n",
|
||||
"\n",
|
||||
"### 使用分詞器的詞彙表\n",
|
||||
"\n",
|
||||
"當使用分詞器的詞彙表時,詞彙表中的某些詞會有對應的 Word2Vec 嵌入,而某些則會缺失。假設我們的詞彙表大小為 `vocab_size`,而 Word2Vec 嵌入向量的長度為 `embed_size`,那麼嵌入層將由一個形狀為 `vocab_size`$\\times$`embed_size` 的權重矩陣表示。我們將通過遍歷詞彙表來填充這個矩陣:\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 9,
|
||||
"metadata": {
|
||||
"tags": []
|
||||
},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"Embedding size: 300\n",
|
||||
"Populating matrix, this will take some time...Done, found 4551 words, 784 words missing\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"embed_size = len(w2v.get_vector('hello'))\n",
|
||||
"print(f'Embedding size: {embed_size}')\n",
|
||||
"\n",
|
||||
"vocab = vectorizer.get_vocabulary()\n",
|
||||
"W = np.zeros((vocab_size,embed_size))\n",
|
||||
"print('Populating matrix, this will take some time...',end='')\n",
|
||||
"found, not_found = 0,0\n",
|
||||
"for i,w in enumerate(vocab):\n",
|
||||
" try:\n",
|
||||
" W[i] = w2v.get_vector(w)\n",
|
||||
" found+=1\n",
|
||||
" except:\n",
|
||||
" # W[i] = np.random.normal(0.0,0.3,size=(embed_size,))\n",
|
||||
" not_found+=1\n",
|
||||
"\n",
|
||||
"print(f\"Done, found {found} words, {not_found} words missing\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"對於不在 Word2Vec 詞彙表中的詞語,我們可以選擇將它們設為零向量,或者生成一個隨機向量。\n",
|
||||
"\n",
|
||||
"現在我們可以定義一個帶有預訓練權重的嵌入層:\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 10,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"emb = keras.layers.Embedding(vocab_size,embed_size,weights=[W],trainable=False)\n",
|
||||
"model = keras.models.Sequential([\n",
|
||||
" vectorizer, emb,\n",
|
||||
" keras.layers.Lambda(lambda x: tf.reduce_mean(x,axis=1)),\n",
|
||||
" keras.layers.Dense(4, activation='softmax')\n",
|
||||
"])"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": []
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 11,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"938/938 [==============================] - 10s 10ms/step - loss: 1.1075 - acc: 0.7822 - val_loss: 0.9134 - val_acc: 0.8175\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"<keras.callbacks.History at 0x2220226ef10>"
|
||||
]
|
||||
},
|
||||
"execution_count": 11,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"model.compile(loss='sparse_categorical_crossentropy',metrics=['acc'])\n",
|
||||
"model.fit(ds_train.map(tupelize).batch(batch_size),\n",
|
||||
" validation_data=ds_test.map(tupelize).batch(batch_size))"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"> **注意**:請注意,我們在建立 `Embedding` 時設置了 `trainable=False`,這表示我們不會重新訓練 Embedding 層。這可能會導致準確度稍微降低,但能加快訓練速度。\n",
|
||||
"\n",
|
||||
"### 使用嵌入詞彙表\n",
|
||||
"\n",
|
||||
"之前方法的一個問題是,TextVectorization 和 Embedding 使用的詞彙表不同。為了解決這個問題,我們可以採用以下其中一種解決方案:\n",
|
||||
"* 重新訓練 Word2Vec 模型以適配我們的詞彙表。\n",
|
||||
"* 使用預訓練 Word2Vec 模型的詞彙表來載入我們的數據集。在載入數據集時,可以指定使用的詞彙表。\n",
|
||||
"\n",
|
||||
"第二種方法看起來更簡單,所以我們來實現它。首先,我們將使用 Word2Vec 嵌入中指定的詞彙表來建立一個 `TextVectorization` 層:\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 12,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"vocab = list(w2v.vocab.keys())\n",
|
||||
"vectorizer = keras.layers.experimental.preprocessing.TextVectorization(input_shape=(1,))\n",
|
||||
"vectorizer.set_vocabulary(vocab)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"gensim 的詞嵌入庫包含一個方便的函數 `get_keras_embeddings`,它會自動為你創建相應的 Keras 嵌入層。\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 13,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"Epoch 1/5\n",
|
||||
"938/938 [==============================] - 20s 14ms/step - loss: 1.3377 - acc: 0.4978 - val_loss: 1.2995 - val_acc: 0.5647\n",
|
||||
"Epoch 2/5\n",
|
||||
"938/938 [==============================] - 10s 10ms/step - loss: 1.2587 - acc: 0.5722 - val_loss: 1.2339 - val_acc: 0.5842\n",
|
||||
"Epoch 3/5\n",
|
||||
"938/938 [==============================] - 10s 10ms/step - loss: 1.1980 - acc: 0.5884 - val_loss: 1.1826 - val_acc: 0.5954\n",
|
||||
"Epoch 4/5\n",
|
||||
"938/938 [==============================] - 12s 13ms/step - loss: 1.1503 - acc: 0.6002 - val_loss: 1.1417 - val_acc: 0.6018\n",
|
||||
"Epoch 5/5\n",
|
||||
"938/938 [==============================] - 11s 12ms/step - loss: 1.1120 - acc: 0.6097 - val_loss: 1.1083 - val_acc: 0.6104\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"<keras.callbacks.History at 0x2220ccb81c0>"
|
||||
]
|
||||
},
|
||||
"execution_count": 13,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"model = keras.models.Sequential([\n",
|
||||
" vectorizer, \n",
|
||||
" w2v.get_keras_embedding(train_embeddings=False),\n",
|
||||
" keras.layers.Lambda(lambda x: tf.reduce_mean(x,axis=1)),\n",
|
||||
" keras.layers.Dense(4, activation='softmax')\n",
|
||||
"])\n",
|
||||
"model.compile(loss='sparse_categorical_crossentropy',metrics=['acc'])\n",
|
||||
"model.fit(ds_train.map(tupelize).batch(128),validation_data=ds_test.map(tupelize).batch(128),epochs=5)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"我們未能看到更高準確度的其中一個原因是因為我們的數據集中的某些詞語在預訓練的GloVe詞彙表中缺失,因此它們基本上被忽略了。為了解決這個問題,我們可以基於我們的數據集訓練自己的嵌入。\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## 語境嵌入\n",
|
||||
"\n",
|
||||
"傳統預訓練嵌入表示(例如 Word2Vec)的一個主要限制是,雖然它們可以捕捉到某些詞語的含義,但無法區分不同的意思。這可能會在下游模型中引起問題。\n",
|
||||
"\n",
|
||||
"例如,單詞「play」在以下兩個句子中有不同的意思:\n",
|
||||
"- 我去劇院看了一場**戲劇**。\n",
|
||||
"- John 想和他的朋友一起**玩**。\n",
|
||||
"\n",
|
||||
"我們提到的預訓練嵌入會將「play」這個詞的兩種意思表示為相同的嵌入。為了解決這個限制,我們需要基於**語言模型**來構建嵌入,語言模型是在大量文本語料上訓練的,並且*知道*單詞如何在不同語境中組合使用。討論語境嵌入超出了本教程的範圍,但我們會在下一單元討論語言模型時回到這個主題。\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"\n---\n\n**免責聲明**: \n本文件已使用人工智能翻譯服務 [Co-op Translator](https://github.com/Azure/co-op-translator) 進行翻譯。我們致力於提供準確的翻譯,但請注意,自動翻譯可能包含錯誤或不準確之處。應以原文文件作為權威來源。對於關鍵資訊,建議尋求專業人工翻譯。我們對因使用此翻譯而引起的任何誤解或誤釋不承擔責任。\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"interpreter": {
|
||||
"hash": "0cb620c6d4b9f7a635928804c26cf22403d89d98d79684e4529119355ee6d5a5"
|
||||
},
|
||||
"kernel_info": {
|
||||
"name": "conda-env-py37_tensorflow-py"
|
||||
},
|
||||
"kernelspec": {
|
||||
"display_name": "py37_tensorflow",
|
||||
"language": "python",
|
||||
"name": "python3"
|
||||
},
|
||||
"language_info": {
|
||||
"codemirror_mode": {
|
||||
"name": "ipython",
|
||||
"version": 3
|
||||
},
|
||||
"file_extension": ".py",
|
||||
"mimetype": "text/x-python",
|
||||
"name": "python",
|
||||
"nbconvert_exporter": "python",
|
||||
"pygments_lexer": "ipython3",
|
||||
"version": "3.8.12"
|
||||
},
|
||||
"nteract": {
|
||||
"version": "nteract-front-end@1.0.0"
|
||||
},
|
||||
"coopTranslator": {
|
||||
"original_hash": "b859482be7f61d1eadc2c6a2720a37e4",
|
||||
"translation_date": "2025-08-31T10:53:44+00:00",
|
||||
"source_file": "lessons/5-NLP/14-Embeddings/EmbeddingsTF.ipynb",
|
||||
"language_code": "hk"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 4
|
||||
}
|
||||
|
|
@ -0,0 +1,68 @@
|
|||
# 嵌入
|
||||
|
||||
## [課前測驗](https://ff-quizzes.netlify.app/en/ai/quiz/27)
|
||||
|
||||
在基於 BoW 或 TF/IDF 訓練分類器時,我們操作的是高維度的詞袋向量,其長度為 `vocab_size`,並且我們明確地將低維度的位置信息表示向量轉換為稀疏的單熱編碼表示。然而,這種單熱編碼表示並不具備記憶效率。此外,每個詞都被獨立對待,即單熱編碼向量無法表達詞與詞之間的語義相似性。
|
||||
|
||||
**嵌入**的概念是用低維度的密集向量來表示詞,這些向量能夠在某種程度上反映詞的語義。我們稍後會討論如何構建有意義的詞嵌入,但現在先將嵌入理解為一種降低詞向量維度的方法。
|
||||
|
||||
嵌入層會將一個詞作為輸入,並生成指定 `embedding_size` 的輸出向量。某種程度上,它與 `Linear` 層非常相似,但嵌入層不需要單熱編碼向量作為輸入,而是可以直接接受詞的編號作為輸入,從而避免創建大型的單熱編碼向量。
|
||||
|
||||
通過在分類器網絡中使用嵌入層作為第一層,我們可以從詞袋模型切換到 **嵌入袋** 模型。在嵌入袋模型中,我們首先將文本中的每個詞轉換為相應的嵌入,然後對所有嵌入計算某種聚合函數,例如 `sum`、`average` 或 `max`。
|
||||
|
||||

|
||||
|
||||
> 圖片由作者提供
|
||||
|
||||
## ✍️ 練習:嵌入
|
||||
|
||||
繼續學習以下筆記本:
|
||||
* [使用 PyTorch 的嵌入](EmbeddingsPyTorch.ipynb)
|
||||
* [使用 TensorFlow 的嵌入](EmbeddingsTF.ipynb)
|
||||
|
||||
## 語義嵌入:Word2Vec
|
||||
|
||||
雖然嵌入層學會了將詞映射到向量表示,但這種表示未必具有太多語義上的意義。我們希望學習一種向量表示,使得相似詞或同義詞在某種向量距離(例如歐幾里得距離)上彼此接近。
|
||||
|
||||
為了實現這一點,我們需要以特定方式在大量文本上預訓練嵌入模型。一種訓練語義嵌入的方法叫做 [Word2Vec](https://en.wikipedia.org/wiki/Word2vec)。它基於兩種主要架構來生成詞的分佈式表示:
|
||||
|
||||
- **連續詞袋** (CBoW) —— 在這種架構中,我們訓練模型根據上下文預測詞。給定 ngram $(W_{-2},W_{-1},W_0,W_1,W_2)$,模型的目標是根據 $(W_{-2},W_{-1},W_1,W_2)$ 預測 $W_0$。
|
||||
- **連續跳詞模型** (Skip-Gram) 則與 CBoW 相反。模型使用上下文窗口中的詞來預測當前詞。
|
||||
|
||||
CBoW 的速度更快,而 Skip-Gram 雖然較慢,但在表示不常見詞方面效果更好。
|
||||
|
||||

|
||||
|
||||
> 圖片來源:[這篇論文](https://arxiv.org/pdf/1301.3781.pdf)
|
||||
|
||||
Word2Vec 預訓練嵌入(以及其他類似模型,例如 GloVe)也可以用於神經網絡中的嵌入層。然而,我們需要處理詞彙表,因為用於預訓練 Word2Vec/GloVe 的詞彙表可能與我們文本語料庫中的詞彙表不同。查看上述筆記本,了解如何解決這個問題。
|
||||
|
||||
## 上下文嵌入
|
||||
|
||||
傳統的預訓練嵌入表示(例如 Word2Vec)的一個主要限制是詞義消歧問題。雖然預訓練嵌入可以捕捉詞在上下文中的部分含義,但每個詞的所有可能含義都被編碼到同一嵌入中。這可能會在下游模型中引發問題,因為許多詞(例如 "play")的含義會根據使用的上下文而有所不同。
|
||||
|
||||
例如,詞 "play" 在以下兩個句子中的含義完全不同:
|
||||
|
||||
- 我去劇院看了一場 **戲劇**。
|
||||
- John 想和朋友一起 **玩**。
|
||||
|
||||
上述預訓練嵌入將 "play" 的這兩種含義表示為同一嵌入。為了解決這一限制,我們需要基於 **語言模型** 構建嵌入,該模型在大量文本語料庫上訓練,並且*了解*詞如何在不同上下文中組合。討論上下文嵌入超出了本教程的範圍,但我們會在課程後續討論語言模型時回到這個話題。
|
||||
|
||||
## 結論
|
||||
|
||||
在本課中,你學習了如何在 TensorFlow 和 PyTorch 中構建和使用嵌入層,以更好地反映詞的語義。
|
||||
|
||||
## 🚀 挑戰
|
||||
|
||||
Word2Vec 已被用於一些有趣的應用,包括生成歌曲歌詞和詩歌。查看 [這篇文章](https://www.politetype.com/blog/word2vec-color-poems),了解作者如何使用 Word2Vec 生成詩歌。還可以觀看 [Dan Shiffmann 的這段視頻](https://www.youtube.com/watch?v=LSS_bos_TPI&ab_channel=TheCodingTrain),了解該技術的另一種解釋。然後嘗試將這些技術應用到你自己的文本語料庫中,或許可以從 Kaggle 獲取數據。
|
||||
|
||||
## [課後測驗](https://ff-quizzes.netlify.app/en/ai/quiz/28)
|
||||
|
||||
## 回顧與自學
|
||||
|
||||
閱讀這篇關於 Word2Vec 的論文:[Efficient Estimation of Word Representations in Vector Space](https://arxiv.org/pdf/1301.3781.pdf)
|
||||
|
||||
## [作業:筆記本](assignment.md)
|
||||
|
||||
---
|
||||
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
# 作業:筆記本
|
||||
|
||||
使用與本課程相關的筆記本(無論是 PyTorch 或 TensorFlow 版本),使用您自己的數據集重新執行,或許可以使用來自 Kaggle 的數據集,並註明來源。重寫筆記本以強調您自己的發現。嘗試使用不同類型的數據集並記錄您的發現,例如使用[這些 Beatles 歌詞](https://www.kaggle.com/datasets/jenlooper/beatles-lyrics)。
|
||||
|
||||
**免責聲明**:
|
||||
本文件已使用人工智能翻譯服務 [Co-op Translator](https://github.com/Azure/co-op-translator) 進行翻譯。我們致力於提供準確的翻譯,但請注意,自動翻譯可能包含錯誤或不準確之處。應以原文文件作為權威來源。如涉及關鍵資訊,建議尋求專業人工翻譯。我們對因使用此翻譯而引起的任何誤解或誤釋概不負責。
|
||||
|
|
@ -0,0 +1,576 @@
|
|||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "NXTSugt6ieXh"
|
||||
},
|
||||
"source": [
|
||||
"## 訓練 CBoW 模型\n",
|
||||
"\n",
|
||||
"此筆記本是 [AI for Beginners Curriculum](http://aka.ms/ai-beginners) 的一部分\n",
|
||||
"\n",
|
||||
"在這個例子中,我們將學習如何訓練 CBoW 語言模型,來獲取我們自己的 Word2Vec 嵌入空間。我們將使用 AG News 數據集作為文本來源。\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"source": [
|
||||
"import torch\n",
|
||||
"import torchtext\n",
|
||||
"import os\n",
|
||||
"import collections\n",
|
||||
"import builtins\n",
|
||||
"import random\n",
|
||||
"import numpy as np"
|
||||
],
|
||||
"metadata": {
|
||||
"id": "q-UiiJUKaxHj"
|
||||
},
|
||||
"execution_count": null,
|
||||
"outputs": []
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"source": [
|
||||
"device = torch.device(\"cuda\" if torch.cuda.is_available() else \"cpu\")"
|
||||
],
|
||||
"metadata": {
|
||||
"id": "TFbR8CZaTZ1q"
|
||||
},
|
||||
"execution_count": null,
|
||||
"outputs": []
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"source": [
|
||||
"首先讓我們載入數據集並定義分詞器和詞彙表。我們將 `vocab_size` 設置為 5000 以稍微限制計算量。\n"
|
||||
],
|
||||
"metadata": {
|
||||
"id": "HIwC7lI5T-ov"
|
||||
}
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"source": [
|
||||
"def load_dataset(ngrams = 1, min_freq = 1, vocab_size = 5000 , lines_cnt = 500):\n",
|
||||
" tokenizer = torchtext.data.utils.get_tokenizer('basic_english')\n",
|
||||
" print(\"Loading dataset...\")\n",
|
||||
" test_dataset, train_dataset = torchtext.datasets.AG_NEWS(root='./data')\n",
|
||||
" train_dataset = list(train_dataset)\n",
|
||||
" test_dataset = list(test_dataset)\n",
|
||||
" classes = ['World', 'Sports', 'Business', 'Sci/Tech']\n",
|
||||
" print('Building vocab...')\n",
|
||||
" counter = collections.Counter()\n",
|
||||
" for i, (_, line) in enumerate(train_dataset):\n",
|
||||
" counter.update(torchtext.data.utils.ngrams_iterator(tokenizer(line),ngrams=ngrams))\n",
|
||||
" if i == lines_cnt:\n",
|
||||
" break\n",
|
||||
" vocab = torchtext.vocab.Vocab(collections.Counter(dict(counter.most_common(vocab_size))), min_freq=min_freq)\n",
|
||||
" return train_dataset, test_dataset, classes, vocab, tokenizer"
|
||||
],
|
||||
"metadata": {
|
||||
"id": "wdZuygtgiuLG"
|
||||
},
|
||||
"execution_count": null,
|
||||
"outputs": []
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"source": [
|
||||
"train_dataset, test_dataset, _, vocab, tokenizer = load_dataset()"
|
||||
],
|
||||
"metadata": {
|
||||
"colab": {
|
||||
"base_uri": "https://localhost:8080/"
|
||||
},
|
||||
"id": "4d1nU1gsivGu",
|
||||
"outputId": "949fe272-ae0e-49f5-c373-6703458b3a74"
|
||||
},
|
||||
"execution_count": null,
|
||||
"outputs": [
|
||||
{
|
||||
"output_type": "stream",
|
||||
"name": "stdout",
|
||||
"text": [
|
||||
"Loading dataset...\n",
|
||||
"Building vocab...\n"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"source": [
|
||||
"def encode(x, vocabulary, tokenizer = tokenizer):\n",
|
||||
" return [vocabulary[s] for s in tokenizer(x)]"
|
||||
],
|
||||
"metadata": {
|
||||
"id": "1XDYNhG8ToFV"
|
||||
},
|
||||
"execution_count": null,
|
||||
"outputs": []
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "LIlQk6_PaHVY"
|
||||
},
|
||||
"source": [
|
||||
"## CBoW 模型\n",
|
||||
"\n",
|
||||
"CBoW 學習根據 $2N$ 個相鄰的詞來預測一個詞。例如,當 $N=1$ 時,我們可以從句子 *I like to train networks* 中得到以下配對:(like, I)、(I, like)、(to, like)、(like, to)、(train, to)、(to, train)、(networks, train)、(train, networks)。在這裡,第一個詞是作為輸入的相鄰詞,第二個詞是我們要預測的詞。\n",
|
||||
"\n",
|
||||
"為了構建一個用來預測下一個詞的網絡,我們需要提供相鄰詞作為輸入,並獲得詞的編號作為輸出。CBoW 網絡的架構如下:\n",
|
||||
"\n",
|
||||
"* 輸入詞會通過嵌入層。這個嵌入層就是我們的 Word2Vec 嵌入,因此我們會將其單獨定義為 `embedder` 變量。在這個例子中,我們會使用嵌入大小為 30,儘管你可能想嘗試更高的維度(真實的 Word2Vec 通常是 300 維)。\n",
|
||||
"* 嵌入向量接著會傳遞到一個線性層,該層將預測輸出詞。因此它有 `vocab_size` 個神經元。\n",
|
||||
"\n",
|
||||
"對於輸出,如果我們使用 `CrossEntropyLoss` 作為損失函數,我們也需要僅提供詞的編號作為期望結果,而不需要使用 one-hot 編碼。\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"source": [
|
||||
"vocab_size = len(vocab)\n",
|
||||
"\n",
|
||||
"embedder = torch.nn.Embedding(num_embeddings = vocab_size, embedding_dim = 30)\n",
|
||||
"model = torch.nn.Sequential(\n",
|
||||
" embedder,\n",
|
||||
" torch.nn.Linear(in_features = 30, out_features = vocab_size),\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"print(model)"
|
||||
],
|
||||
"metadata": {
|
||||
"colab": {
|
||||
"base_uri": "https://localhost:8080/"
|
||||
},
|
||||
"id": "akKTcKQKkfl2",
|
||||
"outputId": "da687e3e-a8ec-4c1a-e456-ab8cd6ac7dad"
|
||||
},
|
||||
"execution_count": null,
|
||||
"outputs": [
|
||||
{
|
||||
"output_type": "stream",
|
||||
"name": "stdout",
|
||||
"text": [
|
||||
"Sequential(\n",
|
||||
" (0): Embedding(5002, 30)\n",
|
||||
" (1): Linear(in_features=30, out_features=5002, bias=True)\n",
|
||||
")\n"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "Nud6jgGPaHVa"
|
||||
},
|
||||
"source": [
|
||||
"## 準備訓練數據\n",
|
||||
"\n",
|
||||
"現在讓我們編寫主要函數,用於從文本中計算 CBoW 詞對。這個函數將允許我們指定窗口大小,並返回一組詞對——輸入詞和輸出詞。請注意,這個函數既可以用於詞,也可以用於向量/張量——這將使我們能夠在將文本傳遞給 `to_cbow` 函數之前對其進行編碼。\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"colab": {
|
||||
"base_uri": "https://localhost:8080/"
|
||||
},
|
||||
"id": "x-dsXygOieXn",
|
||||
"outputId": "c2218280-e540-40ba-9546-efe48d0d714f"
|
||||
},
|
||||
"outputs": [
|
||||
{
|
||||
"output_type": "stream",
|
||||
"name": "stdout",
|
||||
"text": [
|
||||
"[['like', 'I'], ['to', 'I'], ['I', 'like'], ['to', 'like'], ['train', 'like'], ['I', 'to'], ['like', 'to'], ['train', 'to'], ['networks', 'to'], ['like', 'train'], ['to', 'train'], ['networks', 'train'], ['to', 'networks'], ['train', 'networks']]\n",
|
||||
"[[232, 172], [5, 172], [172, 232], [5, 232], [0, 232], [172, 5], [232, 5], [0, 5], [1202, 5], [232, 0], [5, 0], [1202, 0], [5, 1202], [0, 1202]]\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"def to_cbow(sent,window_size=2):\n",
|
||||
" res = []\n",
|
||||
" for i,x in enumerate(sent):\n",
|
||||
" for j in range(max(0,i-window_size),min(i+window_size+1,len(sent))):\n",
|
||||
" if i!=j:\n",
|
||||
" res.append([sent[j],x])\n",
|
||||
" return res\n",
|
||||
"\n",
|
||||
"print(to_cbow(['I','like','to','train','networks']))\n",
|
||||
"print(to_cbow(encode('I like to train networks', vocab)))"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "XVaaDLjaaHVb"
|
||||
},
|
||||
"source": [
|
||||
"讓我們準備訓練數據集。我們將瀏覽所有新聞,調用 `to_cbow` 來獲取單詞對列表,並將這些對添加到 `X` 和 `Y` 中。為了節省時間,我們只考慮前 10k 條新聞項目——如果你有更多時間等待,並希望獲得更好的嵌入,可以輕鬆移除這個限制 :)\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "54b-Gd9TieXo"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"X = []\n",
|
||||
"Y = []\n",
|
||||
"for i, x in zip(range(10000), train_dataset):\n",
|
||||
" for w1, w2 in to_cbow(encode(x[1], vocab), window_size = 5):\n",
|
||||
" X.append(w1)\n",
|
||||
" Y.append(w2)\n",
|
||||
"\n",
|
||||
"X = torch.tensor(X)\n",
|
||||
"Y = torch.tensor(Y)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"source": [
|
||||
"我們還會將該數據轉換為一個數據集,並創建數據加載器:\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": [
|
||||
"我們還會將該數據轉換為一個數據集,並創建數據加載器:\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "AbLUcojlieXo"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"ds = SimpleIterableDataset(X, Y)\n",
|
||||
"dl = torch.utils.data.DataLoader(ds, batch_size = 256)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "pKQr7sXeaHVc"
|
||||
},
|
||||
"source": [
|
||||
"現在讓我們進行實際訓練。我們將使用 `SGD` 優化器,並設定相當高的學習率。你也可以嘗試使用其他優化器,例如 `Adam`。我們將先訓練 10 個周期——如果你希望更低的損失,可以重新執行此單元格。\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"source": [
|
||||
"def train_epoch(net, dataloader, lr = 0.01, optimizer = None, loss_fn = torch.nn.CrossEntropyLoss(), epochs = None, report_freq = 1):\n",
|
||||
" optimizer = optimizer or torch.optim.Adam(net.parameters(), lr = lr)\n",
|
||||
" loss_fn = loss_fn.to(device)\n",
|
||||
" net.train()\n",
|
||||
"\n",
|
||||
" for i in range(epochs):\n",
|
||||
" total_loss, j = 0, 0, \n",
|
||||
" for labels, features in dataloader:\n",
|
||||
" optimizer.zero_grad()\n",
|
||||
" features, labels = features.to(device), labels.to(device)\n",
|
||||
" out = net(features)\n",
|
||||
" loss = loss_fn(out, labels)\n",
|
||||
" loss.backward()\n",
|
||||
" optimizer.step()\n",
|
||||
" total_loss += loss\n",
|
||||
" j += 1\n",
|
||||
" if i % report_freq == 0:\n",
|
||||
" print(f\"Epoch: {i+1}: loss={total_loss.item()/j}\")\n",
|
||||
"\n",
|
||||
" return total_loss.item()/j"
|
||||
],
|
||||
"metadata": {
|
||||
"id": "HeeCYKr_KF1w"
|
||||
},
|
||||
"execution_count": null,
|
||||
"outputs": []
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"source": [
|
||||
"train_epoch(net = model, dataloader = dl, optimizer = torch.optim.SGD(model.parameters(), lr = 0.1), loss_fn = torch.nn.CrossEntropyLoss(), epochs = 10)"
|
||||
],
|
||||
"metadata": {
|
||||
"colab": {
|
||||
"base_uri": "https://localhost:8080/"
|
||||
},
|
||||
"id": "KVgwGtDHgDlT",
|
||||
"outputId": "2447833f-f0e3-4566-c33d-addbfe2f451d"
|
||||
},
|
||||
"execution_count": null,
|
||||
"outputs": [
|
||||
{
|
||||
"output_type": "stream",
|
||||
"name": "stdout",
|
||||
"text": [
|
||||
"Epoch: 1: loss=5.664632366860172\n",
|
||||
"Epoch: 2: loss=5.632101973960962\n",
|
||||
"Epoch: 3: loss=5.610399051405015\n",
|
||||
"Epoch: 4: loss=5.594621561080262\n",
|
||||
"Epoch: 5: loss=5.582538017415446\n",
|
||||
"Epoch: 6: loss=5.572900234519603\n",
|
||||
"Epoch: 7: loss=5.564951676341915\n",
|
||||
"Epoch: 8: loss=5.558288112064614\n",
|
||||
"Epoch: 9: loss=5.552576955031129\n",
|
||||
"Epoch: 10: loss=5.547634165194347\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"output_type": "execute_result",
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"5.547634165194347"
|
||||
]
|
||||
},
|
||||
"metadata": {},
|
||||
"execution_count": 16
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "W8u2qXZmaHVd"
|
||||
},
|
||||
"source": [
|
||||
"## 嘗試使用 Word2Vec\n",
|
||||
"\n",
|
||||
"要使用 Word2Vec,我們先提取與詞彙表中所有單詞對應的向量:\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "r8TatcXjkU_t"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"vectors = torch.stack([embedder(torch.tensor(vocab[s])) for s in vocab.itos], 0)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "3OcX21UOaHVd"
|
||||
},
|
||||
"source": [
|
||||
"讓我們看看,例如,單詞 **Paris** 是如何被編碼成一個向量的:\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"colab": {
|
||||
"base_uri": "https://localhost:8080/"
|
||||
},
|
||||
"id": "bz6tAeLzieXp",
|
||||
"outputId": "5b20850e-4342-45e9-f840-cfac2b4d61d8"
|
||||
},
|
||||
"outputs": [
|
||||
{
|
||||
"output_type": "stream",
|
||||
"name": "stdout",
|
||||
"text": [
|
||||
"tensor([-0.0915, 2.1224, -0.0281, -0.6819, 1.1219, 0.6458, -1.3704, -1.3314,\n",
|
||||
" -1.1437, 0.4496, 0.2301, -0.3515, -0.8485, 1.0481, 0.4386, -0.8949,\n",
|
||||
" 0.5644, 1.0939, -2.5096, 3.2949, -0.2601, -0.8640, 0.1421, -0.0804,\n",
|
||||
" -0.5083, -1.0560, 0.9753, -0.5949, -1.6046, 0.5774],\n",
|
||||
" grad_fn=<EmbeddingBackward>)\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"paris_vec = embedder(torch.tensor(vocab['paris']))\n",
|
||||
"print(paris_vec)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "pHTJlaeYaHVd"
|
||||
},
|
||||
"source": [
|
||||
"使用 Word2Vec 來尋找同義詞是很有趣的。以下函數將返回與給定輸入最接近的 `n` 個詞。為了找到它們,我們計算 $|w_i - v|$ 的範數,其中 $v$ 是與我們輸入詞對應的向量,而 $w_i$ 是詞彙表中第 $i$ 個詞的編碼。我們然後對數組進行排序,並使用 `argsort` 返回相應的索引,接著取列表的前 `n` 個元素,這些元素編碼了詞彙表中最接近詞的位置。\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"colab": {
|
||||
"base_uri": "https://localhost:8080/"
|
||||
},
|
||||
"id": "NlZyi-_olFar",
|
||||
"outputId": "b5dbb163-88c4-4d5a-eaf2-6751f700e98c"
|
||||
},
|
||||
"outputs": [
|
||||
{
|
||||
"output_type": "execute_result",
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"['microsoft', 'quoted', 'lp', 'rate', 'top']"
|
||||
]
|
||||
},
|
||||
"metadata": {},
|
||||
"execution_count": 56
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"def close_words(x, n = 5):\n",
|
||||
" vec = embedder(torch.tensor(vocab[x]))\n",
|
||||
" top5 = np.linalg.norm(vectors.detach().numpy() - vec.detach().numpy(), axis = 1).argsort()[:n]\n",
|
||||
" return [ vocab.itos[x] for x in top5 ]\n",
|
||||
"\n",
|
||||
"close_words('microsoft')"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"colab": {
|
||||
"base_uri": "https://localhost:8080/"
|
||||
},
|
||||
"id": "-dQq7xeAln0U",
|
||||
"outputId": "66f768c3-c248-4bfd-ce4f-c8ffc6d0dd0d"
|
||||
},
|
||||
"outputs": [
|
||||
{
|
||||
"output_type": "execute_result",
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"['basketball', 'lot', 'sinai', 'states', 'healthdaynews']"
|
||||
]
|
||||
},
|
||||
"metadata": {},
|
||||
"execution_count": 51
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"close_words('basketball')"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"colab": {
|
||||
"base_uri": "https://localhost:8080/"
|
||||
},
|
||||
"id": "fJXqK26b29sa",
|
||||
"outputId": "78f0baba-ffd0-485a-dd87-0a12bedfd7fa"
|
||||
},
|
||||
"outputs": [
|
||||
{
|
||||
"output_type": "execute_result",
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"['funds', 'travel', 'sydney', 'japan', 'business']"
|
||||
]
|
||||
},
|
||||
"metadata": {},
|
||||
"execution_count": 77
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"close_words('funds')"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "My0VeTDd3Ji8"
|
||||
},
|
||||
"source": [
|
||||
"## 重點\n",
|
||||
"\n",
|
||||
"透過使用像 CBoW 這樣的巧妙技術,我們可以訓練 Word2Vec 模型。你也可以嘗試訓練 skip-gram 模型,該模型是基於給定中心詞來預測鄰近詞,看看它的表現如何。\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"\n---\n\n**免責聲明**: \n本文件已使用人工智能翻譯服務 [Co-op Translator](https://github.com/Azure/co-op-translator) 進行翻譯。儘管我們致力於提供準確的翻譯,但請注意,自動翻譯可能包含錯誤或不準確之處。原始語言的文件應被視為權威來源。對於重要信息,建議使用專業人工翻譯。我們對因使用此翻譯而引起的任何誤解或錯誤解釋不承擔責任。\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"colab": {
|
||||
"collapsed_sections": [],
|
||||
"name": "CBoW-PyTorch.ipynb",
|
||||
"provenance": []
|
||||
},
|
||||
"interpreter": {
|
||||
"hash": "16af2a8bbb083ea23e5e41c7f5787656b2ce26968575d8763f2c4b17f9cd711f"
|
||||
},
|
||||
"kernelspec": {
|
||||
"display_name": "Python 3.8.12 ('py38')",
|
||||
"language": "python",
|
||||
"name": "python3"
|
||||
},
|
||||
"language_info": {
|
||||
"codemirror_mode": {
|
||||
"name": "ipython",
|
||||
"version": 3
|
||||
},
|
||||
"file_extension": ".py",
|
||||
"mimetype": "text/x-python",
|
||||
"name": "python",
|
||||
"nbconvert_exporter": "python",
|
||||
"pygments_lexer": "ipython3",
|
||||
"version": "3.8.12"
|
||||
},
|
||||
"orig_nbformat": 4,
|
||||
"gpuClass": "standard",
|
||||
"coopTranslator": {
|
||||
"original_hash": "36df28efe3fe40b6fb0a7fa48fe3ea82",
|
||||
"translation_date": "2025-08-31T10:36:54+00:00",
|
||||
"source_file": "lessons/5-NLP/15-LanguageModeling/CBoW-PyTorch.ipynb",
|
||||
"language_code": "hk"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 0
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,45 @@
|
|||
# 語言建模
|
||||
|
||||
語義嵌入,例如 Word2Vec 和 GloVe,實際上是邁向**語言建模**的第一步——創建能夠某種程度上*理解*(或*表示*)語言特性的模型。
|
||||
|
||||
## [課前測驗](https://ff-quizzes.netlify.app/en/ai/quiz/29)
|
||||
|
||||
語言建模的核心思想是以無監督的方式在未標註的數據集上進行訓練。這很重要,因為我們擁有大量未標註的文本,而標註文本的數量則受限於我們能投入的標註工作量。通常,我們可以構建能夠**預測文本中缺失詞語**的語言模型,因為隨機遮蔽文本中的某個詞語並將其作為訓練樣本是一件很容易的事。
|
||||
|
||||
## 嵌入訓練
|
||||
|
||||
在之前的例子中,我們使用了預訓練的語義嵌入,但了解這些嵌入是如何訓練的也非常有趣。有幾種可能的思路可以用於訓練:
|
||||
|
||||
* **N-Gram** 語言建模,通過查看前 N 個詞元來預測當前詞元(N-gram)。
|
||||
* **連續詞袋模型** (CBoW),通過預測詞元序列 $W_{-N}$, ..., $W_N$ 中的中間詞元 $W_0$。
|
||||
* **Skip-gram**,通過中間詞元 $W_0$ 預測一組相鄰詞元 {$W_{-N},\dots, W_{-1}, W_1,\dots, W_N$}。
|
||||
|
||||

|
||||
|
||||
> 圖片來源:[這篇論文](https://arxiv.org/pdf/1301.3781.pdf)
|
||||
|
||||
## ✍️ 示例筆記本:訓練 CBoW 模型
|
||||
|
||||
繼續學習以下筆記本:
|
||||
|
||||
* [使用 TensorFlow 訓練 CBoW Word2Vec](CBoW-TF.ipynb)
|
||||
* [使用 PyTorch 訓練 CBoW Word2Vec](CBoW-PyTorch.ipynb)
|
||||
|
||||
## 結論
|
||||
|
||||
在上一課中,我們看到詞嵌入的效果就像魔法一樣!現在我們知道,訓練詞嵌入並不是一項非常複雜的任務,如果需要,我們應該能夠為特定領域的文本訓練自己的詞嵌入。
|
||||
|
||||
## [課後測驗](https://ff-quizzes.netlify.app/en/ai/quiz/30)
|
||||
|
||||
## 回顧與自學
|
||||
|
||||
* [PyTorch 官方語言建模教程](https://pytorch.org/tutorials/beginner/nlp/word_embeddings_tutorial.html)。
|
||||
* [TensorFlow 官方訓練 Word2Vec 模型教程](https://www.TensorFlow.org/tutorials/text/word2vec)。
|
||||
* 使用 **gensim** 框架訓練最常用的嵌入,只需幾行代碼,詳情請參閱[此文檔](https://pytorch.org/tutorials/beginner/nlp/word_embeddings_tutorial.html)。
|
||||
|
||||
## 🚀 [作業:訓練 Skip-Gram 模型](lab/README.md)
|
||||
|
||||
在實驗中,我們挑戰你修改本課的代碼,將 CBoW 模型改為 Skip-Gram 模型進行訓練。[閱讀詳情](lab/README.md)
|
||||
|
||||
---
|
||||
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
# 訓練 Skip-Gram 模型
|
||||
|
||||
來自 [AI for Beginners Curriculum](https://github.com/microsoft/ai-for-beginners) 的實驗課題。
|
||||
|
||||
## 任務
|
||||
|
||||
在這次實驗中,我們挑戰你使用 Skip-Gram 技術訓練 Word2Vec 模型。訓練一個嵌入式網絡來預測 $N$ 個詞寬的 Skip-Gram 窗口中的相鄰詞。你可以使用[本課程的代碼](../../../../../../lessons/5-NLP/15-LanguageModeling/CBoW-TF.ipynb),並稍作修改。
|
||||
|
||||
## 數據集
|
||||
|
||||
你可以使用任何一本書作為數據集。在 [Project Gutenberg](https://www.gutenberg.org/) 上可以找到許多免費文本,例如,這裡有 Lewis Carroll 的《愛麗絲夢遊仙境》的[直接鏈接](https://www.gutenberg.org/files/11/11-0.txt)。或者,你也可以使用莎士比亞的戲劇,以下代碼可以幫助你獲取:
|
||||
|
||||
```python
|
||||
path_to_file = tf.keras.utils.get_file(
|
||||
'shakespeare.txt',
|
||||
'https://storage.googleapis.com/download.tensorflow.org/data/shakespeare.txt')
|
||||
text = open(path_to_file, 'rb').read().decode(encoding='utf-8')
|
||||
```
|
||||
|
||||
## 探索!
|
||||
|
||||
如果你有時間並希望深入了解這個主題,可以嘗試探索以下幾點:
|
||||
|
||||
* 嵌入尺寸如何影響結果?
|
||||
* 不同的文本風格如何影響結果?
|
||||
* 選擇幾個非常不同類型的詞及其同義詞,獲取它們的向量表示,應用 PCA 將維度降至 2,並在 2D 空間中繪製它們。你能看到任何模式嗎?
|
||||
|
||||
**免責聲明**:
|
||||
本文件使用人工智能翻譯服務 [Co-op Translator](https://github.com/Azure/co-op-translator) 進行翻譯。我們致力於提供準確的翻譯,但請注意,自動翻譯可能包含錯誤或不準確之處。應以原文文件作為權威來源。對於關鍵資訊,建議尋求專業人工翻譯。我們對因使用此翻譯而引起的任何誤解或誤釋概不負責。
|
||||
|
|
@ -0,0 +1,87 @@
|
|||
# 循環神經網絡
|
||||
|
||||
## [課前測驗](https://ff-quizzes.netlify.app/en/ai/quiz/31)
|
||||
|
||||
在之前的章節中,我們使用了文本的豐富語義表示以及嵌入層上的簡單線性分類器。這種架構能夠捕捉句子中詞語的聚合意義,但它並未考慮詞語的**順序**,因為嵌入層上的聚合操作已經移除了原始文本中的這些信息。由於這些模型無法建模詞語的順序,因此它們無法解決更複雜或更具歧義的任務,例如文本生成或問題回答。
|
||||
|
||||
為了捕捉文本序列的意義,我們需要使用另一種神經網絡架構,稱為**循環神經網絡**(Recurrent Neural Network,簡稱 RNN)。在 RNN 中,我們將句子逐個符號地輸入網絡,網絡會生成某種**狀態**,然後將該狀態與下一個符號一起再次輸入網絡。
|
||||
|
||||

|
||||
|
||||
> 圖片由作者提供
|
||||
|
||||
給定輸入序列的標記 X<sub>0</sub>,...,X<sub>n</sub>,RNN 會創建一系列神經網絡塊,並通過反向傳播端到端地訓練這個序列。每個網絡塊以一對 (X<sub>i</sub>,S<sub>i</sub>) 作為輸入,並生成 S<sub>i+1</sub> 作為結果。最終的狀態 S<sub>n</sub> 或 (輸出 Y<sub>n</sub>) 會進入線性分類器以生成結果。所有的網絡塊共享相同的權重,並通過一次反向傳播端到端地訓練。
|
||||
|
||||
由於狀態向量 S<sub>0</sub>,...,S<sub>n</sub> 是通過網絡傳遞的,因此它能夠學習詞語之間的序列依賴性。例如,當某個詞語 *not* 出現在序列中時,它可以學習在狀態向量中否定某些元素,從而實現否定。
|
||||
|
||||
> ✅ 由於上圖中所有 RNN 塊的權重是共享的,因此同一圖可以表示為右側的一個塊,並帶有一個循環反饋回路,將網絡的輸出狀態返回到輸入。
|
||||
|
||||
## RNN 單元的結構
|
||||
|
||||
讓我們來看看一個簡單的 RNN 單元是如何組織的。它接受前一狀態 S<sub>i-1</sub> 和當前符號 X<sub>i</sub> 作為輸入,並需要生成輸出狀態 S<sub>i</sub>(有時我們也對其他輸出 Y<sub>i</sub> 感興趣,例如在生成網絡的情況下)。
|
||||
|
||||
一個簡單的 RNN 單元內部有兩個權重矩陣:一個用於轉換輸入符號(我們稱之為 W),另一個用於轉換輸入狀態(H)。在這種情況下,網絡的輸出計算公式為 σ(W×X<sub>i</sub>+H×S<sub>i-1</sub>+b),其中 σ 是激活函數,b 是額外的偏置。
|
||||
|
||||
<img alt="RNN 單元結構" src="../../../../../translated_images/zh-HK/rnn-anatomy.79ee3f3920b3294b.webp" width="50%"/>
|
||||
|
||||
> 圖片由作者提供
|
||||
|
||||
在許多情況下,輸入標記在進入 RNN 之前會通過嵌入層以降低維度。在這種情況下,如果輸入向量的維度是 *emb_size*,而狀態向量的維度是 *hid_size*,則 W 的大小為 *emb_size*×*hid_size*,H 的大小為 *hid_size*×*hid_size*。
|
||||
|
||||
## 長短期記憶(LSTM)
|
||||
|
||||
經典 RNN 的主要問題之一是所謂的**梯度消失**問題。由於 RNN 是通過一次反向傳播端到端地訓練的,因此它很難將誤差傳遞到網絡的第一層,從而無法學習遠距離標記之間的關係。解決這個問題的一種方法是通過使用所謂的**門**引入**顯式狀態管理**。有兩種著名的架構:**長短期記憶**(Long Short Term Memory,簡稱 LSTM)和**門控遞歸單元**(Gated Relay Unit,簡稱 GRU)。
|
||||
|
||||

|
||||
|
||||
> 圖片來源待定
|
||||
|
||||
LSTM 網絡的組織方式與 RNN 類似,但有兩個狀態會從層到層傳遞:實際狀態 C 和隱藏向量 H。在每個單元中,隱藏向量 H<sub>i</sub> 與輸入 X<sub>i</sub> 進行拼接,並通過**門**控制狀態 C 的變化。每個門都是一個具有 sigmoid 激活(輸出範圍為 [0,1])的神經網絡,可以被視為在與狀態向量相乘時的逐位掩碼。以下是各個門的功能(從左到右對應上圖):
|
||||
|
||||
* **遺忘門**接收隱藏向量並決定需要忘記狀態向量 C 的哪些部分,以及需要保留哪些部分。
|
||||
* **輸入門**從輸入和隱藏向量中提取一些信息並插入到狀態中。
|
||||
* **輸出門**通過具有 *tanh* 激活的線性層轉換狀態,然後使用隱藏向量 H<sub>i</sub> 選擇其部分組件以生成新狀態 C<sub>i+1</sub>。
|
||||
|
||||
狀態 C 的組件可以被視為一些可以開啟或關閉的標誌。例如,當我們在序列中遇到名字 *Alice* 時,我們可能會假設它指的是一個女性角色,並在狀態中設置一個標誌,表示句子中有一個女性名詞。當我們進一步遇到短語 *and Tom* 時,我們會設置一個標誌,表示句子中有一個複數名詞。因此,通過操控狀態,我們可以追蹤句子部分的語法屬性。
|
||||
|
||||
> ✅ 理解 LSTM 內部結構的一個極佳資源是 Christopher Olah 的這篇精彩文章 [Understanding LSTM Networks](https://colah.github.io/posts/2015-08-Understanding-LSTMs/)。
|
||||
|
||||
## 雙向和多層 RNN
|
||||
|
||||
我們已經討論了單向運行的循環網絡,從序列的開始到結束。這看起來很自然,因為它類似於我們閱讀和聽取語音的方式。然而,由於在許多實際情況下我們可以隨機訪問輸入序列,因此同時進行雙向的循環計算可能更有意義。這樣的網絡稱為**雙向** RNN。在處理雙向網絡時,我們需要兩個隱藏狀態向量,每個方向一個。
|
||||
|
||||
循環網絡,無論是單向還是雙向,都能捕捉序列中的某些模式,並將其存儲到狀態向量中或傳遞到輸出中。與卷積網絡類似,我們可以在第一層之上構建另一個循環層,以捕捉更高層次的模式,並基於第一層提取的低層次模式進行構建。這引出了**多層 RNN**的概念,它由兩個或更多循環網絡組成,其中前一層的輸出作為下一層的輸入。
|
||||
|
||||

|
||||
|
||||
*圖片來自 [這篇精彩文章](https://towardsdatascience.com/from-a-lstm-cell-to-a-multilayer-lstm-network-with-pytorch-2899eb5696f3) 作者 Fernando López*
|
||||
|
||||
## ✍️ 練習:嵌入
|
||||
|
||||
在以下筆記本中繼續學習:
|
||||
|
||||
* [使用 PyTorch 的 RNN](RNNPyTorch.ipynb)
|
||||
* [使用 TensorFlow 的 RNN](RNNTF.ipynb)
|
||||
|
||||
## 結論
|
||||
|
||||
在本單元中,我們了解到 RNN 可用於序列分類,但事實上,它們可以處理更多任務,例如文本生成、機器翻譯等。我們將在下一單元中探討這些任務。
|
||||
|
||||
## 🚀 挑戰
|
||||
|
||||
閱讀一些關於 LSTM 的文獻並考慮其應用:
|
||||
|
||||
- [Grid Long Short-Term Memory](https://arxiv.org/pdf/1507.01526v1.pdf)
|
||||
- [Show, Attend and Tell: Neural Image Caption
|
||||
Generation with Visual Attention](https://arxiv.org/pdf/1502.03044v2.pdf)
|
||||
|
||||
## [課後測驗](https://ff-quizzes.netlify.app/en/ai/quiz/32)
|
||||
|
||||
## 回顧與自學
|
||||
|
||||
- [Understanding LSTM Networks](https://colah.github.io/posts/2015-08-Understanding-LSTMs/) 作者 Christopher Olah。
|
||||
|
||||
## [作業:筆記本](assignment.md)
|
||||
|
||||
---
|
||||
|
||||
|
|
@ -0,0 +1,479 @@
|
|||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"# 循環神經網絡\n",
|
||||
"\n",
|
||||
"在上一個模組中,我們使用了豐富的語義表示來處理文本,並在嵌入層之上使用了一個簡單的線性分類器。這種架構能夠捕捉句子中詞語的聚合意義,但它並未考慮詞語的**順序**,因為嵌入層上的聚合操作已經將原始文本中的這種信息移除了。由於這些模型無法建模詞語的順序,因此它們無法解決更複雜或更具歧義的任務,例如文本生成或問答系統。\n",
|
||||
"\n",
|
||||
"為了捕捉文本序列的意義,我們需要使用另一種神經網絡架構,稱為**循環神經網絡**(Recurrent Neural Network,簡稱 RNN)。在 RNN 中,我們將句子逐個符號地傳遞給網絡,網絡會生成某種**狀態**,然後我們將該狀態與下一個符號一起再次傳遞給網絡。\n",
|
||||
"\n",
|
||||
"給定輸入的符號序列 $X_0,\\dots,X_n$,RNN 會創建一個神經網絡塊的序列,並通過反向傳播對這個序列進行端到端的訓練。每個網絡塊接收一對 $(X_i,S_i)$ 作為輸入,並生成 $S_{i+1}$ 作為結果。最終的狀態 $S_n$ 或輸出 $X_n$ 會進入線性分類器以生成結果。所有的網絡塊共享相同的權重,並通過一次反向傳播進行端到端的訓練。\n",
|
||||
"\n",
|
||||
"由於狀態向量 $S_0,\\dots,S_n$ 會在網絡中傳遞,因此它能夠學習詞語之間的順序依賴關係。例如,當單詞 *not* 出現在序列中的某處時,網絡可以學會在狀態向量中否定某些元素,從而實現否定的效果。\n",
|
||||
"\n",
|
||||
"> 由於圖片中所有 RNN 塊的權重是共享的,因此同一張圖片可以表示為一個帶有循環反饋迴路的單一塊(右側),該迴路將網絡的輸出狀態傳回到輸入。\n",
|
||||
"\n",
|
||||
"現在讓我們看看循環神經網絡如何幫助我們對新聞數據集進行分類。\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 1,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"Loading dataset...\n",
|
||||
"Building vocab...\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"import torch\n",
|
||||
"import torchtext\n",
|
||||
"from torchnlp import *\n",
|
||||
"train_dataset, test_dataset, classes, vocab = load_dataset()\n",
|
||||
"vocab_size = len(vocab)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## 簡單 RNN 分類器\n",
|
||||
"\n",
|
||||
"在簡單 RNN 的情況下,每個循環單元是一個簡單的線性網絡,它接收連接的輸入向量和狀態向量,並生成一個新的狀態向量。PyTorch 使用 `RNNCell` 類來表示這個單元,而由這些單元組成的網絡則表示為 `RNN` 層。\n",
|
||||
"\n",
|
||||
"為了定義一個 RNN 分類器,我們首先會應用一個嵌入層來降低輸入詞彙的維度,然後在其上添加一個 RNN 層:\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 2,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"class RNNClassifier(torch.nn.Module):\n",
|
||||
" def __init__(self, vocab_size, embed_dim, hidden_dim, num_class):\n",
|
||||
" super().__init__()\n",
|
||||
" self.hidden_dim = hidden_dim\n",
|
||||
" self.embedding = torch.nn.Embedding(vocab_size, embed_dim)\n",
|
||||
" self.rnn = torch.nn.RNN(embed_dim,hidden_dim,batch_first=True)\n",
|
||||
" self.fc = torch.nn.Linear(hidden_dim, num_class)\n",
|
||||
"\n",
|
||||
" def forward(self, x):\n",
|
||||
" batch_size = x.size(0)\n",
|
||||
" x = self.embedding(x)\n",
|
||||
" x,h = self.rnn(x)\n",
|
||||
" return self.fc(x.mean(dim=1))"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"> **注意:** 為了簡化,我們在這裡使用未經訓練的嵌入層,但如果想要更好的效果,可以使用預訓練的嵌入層,例如 Word2Vec 或 GloVe 嵌入,這在之前的單元中已經描述過。為了更好地理解,您可能需要調整此程式碼以使用預訓練的嵌入。\n",
|
||||
"\n",
|
||||
"在我們的情況下,我們將使用填充的數據加載器,因此每個批次都會包含一些相同長度的填充序列。RNN 層將接收嵌入張量的序列,並產生兩個輸出:\n",
|
||||
"* $x$ 是每一步中 RNN 單元輸出的序列\n",
|
||||
"* $h$ 是序列最後一個元素的最終隱藏狀態\n",
|
||||
"\n",
|
||||
"接著,我們應用一個全連接的線性分類器來獲得類別數。\n",
|
||||
"\n",
|
||||
"> **注意:** RNN 的訓練相當困難,因為一旦 RNN 單元沿著序列長度展開,反向傳播所涉及的層數會非常多。因此,我們需要選擇較小的學習率,並在更大的數據集上訓練網絡以獲得良好的結果。這可能需要相當長的時間,因此建議使用 GPU。\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 3,
|
||||
"metadata": {
|
||||
"scrolled": true
|
||||
},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"3200: acc=0.3090625\n",
|
||||
"6400: acc=0.38921875\n",
|
||||
"9600: acc=0.4590625\n",
|
||||
"12800: acc=0.511953125\n",
|
||||
"16000: acc=0.5506875\n",
|
||||
"19200: acc=0.57921875\n",
|
||||
"22400: acc=0.6070089285714285\n",
|
||||
"25600: acc=0.6304296875\n",
|
||||
"28800: acc=0.6484027777777778\n",
|
||||
"32000: acc=0.66509375\n",
|
||||
"35200: acc=0.6790056818181818\n",
|
||||
"38400: acc=0.6929166666666666\n",
|
||||
"41600: acc=0.7035817307692308\n",
|
||||
"44800: acc=0.7137276785714286\n",
|
||||
"48000: acc=0.72225\n",
|
||||
"51200: acc=0.73001953125\n",
|
||||
"54400: acc=0.7372794117647059\n",
|
||||
"57600: acc=0.7436631944444444\n",
|
||||
"60800: acc=0.7503947368421052\n",
|
||||
"64000: acc=0.75634375\n",
|
||||
"67200: acc=0.7615773809523809\n",
|
||||
"70400: acc=0.7662642045454545\n",
|
||||
"73600: acc=0.7708423913043478\n",
|
||||
"76800: acc=0.7751822916666666\n",
|
||||
"80000: acc=0.7790625\n",
|
||||
"83200: acc=0.7825\n",
|
||||
"86400: acc=0.7858564814814815\n",
|
||||
"89600: acc=0.7890513392857142\n",
|
||||
"92800: acc=0.7920474137931034\n",
|
||||
"96000: acc=0.7952708333333334\n",
|
||||
"99200: acc=0.7982258064516129\n",
|
||||
"102400: acc=0.80099609375\n",
|
||||
"105600: acc=0.8037594696969697\n",
|
||||
"108800: acc=0.8060569852941176\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"train_loader = torch.utils.data.DataLoader(train_dataset, batch_size=16, collate_fn=padify, shuffle=True)\n",
|
||||
"net = RNNClassifier(vocab_size,64,32,len(classes)).to(device)\n",
|
||||
"train_epoch(net,train_loader, lr=0.001)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## 長短期記憶網絡 (LSTM)\n",
|
||||
"\n",
|
||||
"傳統 RNN 的主要問題之一是所謂的 **梯度消失** 問題。由於 RNN 是通過一次反向傳播從頭到尾進行訓練的,因此在將誤差傳遞到網絡的第一層時會遇到困難,導致網絡無法學習遠距離的詞元之間的關係。為了解決這個問題,可以通過使用所謂的 **門控機制** 引入 **顯式狀態管理**。其中最知名的兩種架構是 **長短期記憶網絡** (LSTM) 和 **門控循環單元** (GRU)。\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"LSTM 網絡的結構與 RNN 類似,但有兩個狀態會從一層傳遞到下一層:實際狀態 $c$ 和隱藏向量 $h$。在每個單元中,隱藏向量 $h_i$ 與輸入 $x_i$ 拼接在一起,並通過 **門控機制** 控制狀態 $c$ 的變化。每個門控機制都是一個帶有 sigmoid 激活函數(輸出範圍為 $[0,1]$)的神經網絡,可以將其視為在與狀態向量相乘時的位掩碼。以下是這些門控機制(如上圖從左到右所示):\n",
|
||||
"* **遺忘門** 接收隱藏向量並決定向量 $c$ 的哪些部分需要遺忘,哪些需要保留。\n",
|
||||
"* **輸入門** 從輸入和隱藏向量中提取一些信息,並將其插入到狀態中。\n",
|
||||
"* **輸出門** 通過帶有 $\\tanh$ 激活函數的線性層轉換狀態,然後使用隱藏向量 $h_i$ 選擇其部分組件以生成新的狀態 $c_{i+1}$。\n",
|
||||
"\n",
|
||||
"狀態 $c$ 的組件可以被視為一些可以開啟或關閉的標誌。例如,當我們在序列中遇到名字 *Alice* 時,我們可能會假設它指的是女性角色,並在狀態中設置一個標誌,表示句子中有一個女性名詞。當我們進一步遇到短語 *and Tom* 時,我們會設置一個標誌,表示句子中有複數名詞。因此,通過操控狀態,我們可以追蹤句子部分的語法屬性。\n",
|
||||
"\n",
|
||||
"> **Note**: 理解 LSTM 內部結構的一個很棒的資源是 Christopher Olah 的這篇文章 [Understanding LSTM Networks](https://colah.github.io/posts/2015-08-Understanding-LSTMs/)。\n",
|
||||
"\n",
|
||||
"雖然 LSTM 單元的內部結構看起來很複雜,但 PyTorch 將這些實現隱藏在 `LSTMCell` 類中,並提供了 `LSTM` 對象來表示整個 LSTM 層。因此,LSTM 分類器的實現將與我們之前看到的簡單 RNN 非常相似:\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 4,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"class LSTMClassifier(torch.nn.Module):\n",
|
||||
" def __init__(self, vocab_size, embed_dim, hidden_dim, num_class):\n",
|
||||
" super().__init__()\n",
|
||||
" self.hidden_dim = hidden_dim\n",
|
||||
" self.embedding = torch.nn.Embedding(vocab_size, embed_dim)\n",
|
||||
" self.embedding.weight.data = torch.randn_like(self.embedding.weight.data)-0.5\n",
|
||||
" self.rnn = torch.nn.LSTM(embed_dim,hidden_dim,batch_first=True)\n",
|
||||
" self.fc = torch.nn.Linear(hidden_dim, num_class)\n",
|
||||
"\n",
|
||||
" def forward(self, x):\n",
|
||||
" batch_size = x.size(0)\n",
|
||||
" x = self.embedding(x)\n",
|
||||
" x,(h,c) = self.rnn(x)\n",
|
||||
" return self.fc(h[-1])"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": []
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 5,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"3200: acc=0.259375\n",
|
||||
"6400: acc=0.25859375\n",
|
||||
"9600: acc=0.26177083333333334\n",
|
||||
"12800: acc=0.2784375\n",
|
||||
"16000: acc=0.313\n",
|
||||
"19200: acc=0.3528645833333333\n",
|
||||
"22400: acc=0.3965625\n",
|
||||
"25600: acc=0.4385546875\n",
|
||||
"28800: acc=0.4752777777777778\n",
|
||||
"32000: acc=0.505375\n",
|
||||
"35200: acc=0.5326704545454546\n",
|
||||
"38400: acc=0.5557552083333334\n",
|
||||
"41600: acc=0.5760817307692307\n",
|
||||
"44800: acc=0.5954910714285714\n",
|
||||
"48000: acc=0.6118333333333333\n",
|
||||
"51200: acc=0.62681640625\n",
|
||||
"54400: acc=0.6404779411764706\n",
|
||||
"57600: acc=0.6520138888888889\n",
|
||||
"60800: acc=0.662828947368421\n",
|
||||
"64000: acc=0.673546875\n",
|
||||
"67200: acc=0.6831547619047619\n",
|
||||
"70400: acc=0.6917897727272727\n",
|
||||
"73600: acc=0.6997146739130434\n",
|
||||
"76800: acc=0.707109375\n",
|
||||
"80000: acc=0.714075\n",
|
||||
"83200: acc=0.7209134615384616\n",
|
||||
"86400: acc=0.727037037037037\n",
|
||||
"89600: acc=0.7326674107142858\n",
|
||||
"92800: acc=0.7379633620689655\n",
|
||||
"96000: acc=0.7433645833333333\n",
|
||||
"99200: acc=0.7479032258064516\n",
|
||||
"102400: acc=0.752119140625\n",
|
||||
"105600: acc=0.7562405303030303\n",
|
||||
"108800: acc=0.76015625\n",
|
||||
"112000: acc=0.7641339285714286\n",
|
||||
"115200: acc=0.7677777777777778\n",
|
||||
"118400: acc=0.7711233108108108\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"(0.03487814127604167, 0.7728)"
|
||||
]
|
||||
},
|
||||
"execution_count": 5,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"net = LSTMClassifier(vocab_size,64,32,len(classes)).to(device)\n",
|
||||
"train_epoch(net,train_loader, lr=0.001)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## 打包序列\n",
|
||||
"\n",
|
||||
"在我們的例子中,我們需要用零向量填充小批量中的所有序列。雖然這會導致一些記憶體浪費,但對於 RNN 而言,更關鍵的是為填充的輸入項目創建了額外的 RNN 單元,這些單元參與了訓練,但並未攜帶任何重要的輸入資訊。如果能僅針對實際的序列長度來訓練 RNN,效果會更好。\n",
|
||||
"\n",
|
||||
"為此,PyTorch 引入了一種特殊格式來存儲填充的序列。假設我們有一個填充過的小批量輸入,看起來像這樣:\n",
|
||||
"```\n",
|
||||
"[[1,2,3,4,5],\n",
|
||||
" [6,7,8,0,0],\n",
|
||||
" [9,0,0,0,0]]\n",
|
||||
"```\n",
|
||||
"這裡的 0 代表填充的值,而輸入序列的實際長度向量是 `[5,3,1]`。\n",
|
||||
"\n",
|
||||
"為了有效地用填充序列訓練 RNN,我們希望先用較大的小批量(`[1,6,9]`)開始訓練第一組 RNN 單元,然後結束第三個序列的處理,並繼續用較小的小批量(`[2,7]`,`[3,8]`)進行訓練,依此類推。因此,打包序列被表示為一個向量——在我們的例子中是 `[1,6,9,2,7,3,8,4,5]`,以及長度向量(`[5,3,1]`),我們可以根據這些輕鬆重建原始的填充小批量。\n",
|
||||
"\n",
|
||||
"要生成打包序列,我們可以使用 `torch.nn.utils.rnn.pack_padded_sequence` 函數。所有的循環層,包括 RNN、LSTM 和 GRU,都支持將打包序列作為輸入,並生成打包輸出,這些輸出可以通過 `torch.nn.utils.rnn.pad_packed_sequence` 解碼。\n",
|
||||
"\n",
|
||||
"為了能夠生成打包序列,我們需要將長度向量傳遞給網絡,因此我們需要一個不同的函數來準備小批量:\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 6,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"def pad_length(b):\n",
|
||||
" # build vectorized sequence\n",
|
||||
" v = [encode(x[1]) for x in b]\n",
|
||||
" # compute max length of a sequence in this minibatch and length sequence itself\n",
|
||||
" len_seq = list(map(len,v))\n",
|
||||
" l = max(len_seq)\n",
|
||||
" return ( # tuple of three tensors - labels, padded features, length sequence\n",
|
||||
" torch.LongTensor([t[0]-1 for t in b]),\n",
|
||||
" torch.stack([torch.nn.functional.pad(torch.tensor(t),(0,l-len(t)),mode='constant',value=0) for t in v]),\n",
|
||||
" torch.tensor(len_seq)\n",
|
||||
" )\n",
|
||||
"\n",
|
||||
"train_loader_len = torch.utils.data.DataLoader(train_dataset, batch_size=16, collate_fn=pad_length, shuffle=True)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"實際的網絡會與上面的 `LSTMClassifier` 非常相似,但在 `forward` 傳遞時,會同時接收填充過的小批量數據以及序列長度的向量。在計算嵌入後,我們會計算打包序列,將其傳遞給 LSTM 層,然後再將結果解包回來。\n",
|
||||
"\n",
|
||||
"> **注意**:我們實際上並沒有使用解包後的結果 `x`,因為在後續的計算中,我們使用的是隱藏層的輸出。因此,我們可以完全移除這段代碼中的解包部分。我們之所以將其放在這裡,是為了方便你在需要使用網絡輸出進行進一步計算時,能夠輕鬆修改這段代碼。\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 7,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"class LSTMPackClassifier(torch.nn.Module):\n",
|
||||
" def __init__(self, vocab_size, embed_dim, hidden_dim, num_class):\n",
|
||||
" super().__init__()\n",
|
||||
" self.hidden_dim = hidden_dim\n",
|
||||
" self.embedding = torch.nn.Embedding(vocab_size, embed_dim)\n",
|
||||
" self.embedding.weight.data = torch.randn_like(self.embedding.weight.data)-0.5\n",
|
||||
" self.rnn = torch.nn.LSTM(embed_dim,hidden_dim,batch_first=True)\n",
|
||||
" self.fc = torch.nn.Linear(hidden_dim, num_class)\n",
|
||||
"\n",
|
||||
" def forward(self, x, lengths):\n",
|
||||
" batch_size = x.size(0)\n",
|
||||
" x = self.embedding(x)\n",
|
||||
" pad_x = torch.nn.utils.rnn.pack_padded_sequence(x,lengths,batch_first=True,enforce_sorted=False)\n",
|
||||
" pad_x,(h,c) = self.rnn(pad_x)\n",
|
||||
" x, _ = torch.nn.utils.rnn.pad_packed_sequence(pad_x,batch_first=True)\n",
|
||||
" return self.fc(h[-1])"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": []
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 8,
|
||||
"metadata": {
|
||||
"scrolled": true
|
||||
},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"3200: acc=0.285625\n",
|
||||
"6400: acc=0.33359375\n",
|
||||
"9600: acc=0.3876041666666667\n",
|
||||
"12800: acc=0.44078125\n",
|
||||
"16000: acc=0.4825\n",
|
||||
"19200: acc=0.5235416666666667\n",
|
||||
"22400: acc=0.5559821428571429\n",
|
||||
"25600: acc=0.58609375\n",
|
||||
"28800: acc=0.6116666666666667\n",
|
||||
"32000: acc=0.63340625\n",
|
||||
"35200: acc=0.6525284090909091\n",
|
||||
"38400: acc=0.668515625\n",
|
||||
"41600: acc=0.6822596153846154\n",
|
||||
"44800: acc=0.6948214285714286\n",
|
||||
"48000: acc=0.7052708333333333\n",
|
||||
"51200: acc=0.71521484375\n",
|
||||
"54400: acc=0.7239889705882353\n",
|
||||
"57600: acc=0.7315277777777778\n",
|
||||
"60800: acc=0.7388486842105263\n",
|
||||
"64000: acc=0.74571875\n",
|
||||
"67200: acc=0.7518303571428572\n",
|
||||
"70400: acc=0.7576988636363636\n",
|
||||
"73600: acc=0.7628940217391305\n",
|
||||
"76800: acc=0.7681510416666667\n",
|
||||
"80000: acc=0.7728125\n",
|
||||
"83200: acc=0.7772235576923077\n",
|
||||
"86400: acc=0.7815393518518519\n",
|
||||
"89600: acc=0.7857700892857142\n",
|
||||
"92800: acc=0.7895043103448276\n",
|
||||
"96000: acc=0.7930520833333333\n",
|
||||
"99200: acc=0.7959072580645161\n",
|
||||
"102400: acc=0.798994140625\n",
|
||||
"105600: acc=0.802064393939394\n",
|
||||
"108800: acc=0.8051378676470589\n",
|
||||
"112000: acc=0.8077857142857143\n",
|
||||
"115200: acc=0.8104600694444445\n",
|
||||
"118400: acc=0.8128293918918919\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"(0.029785829671223958, 0.8138166666666666)"
|
||||
]
|
||||
},
|
||||
"execution_count": 8,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"net = LSTMPackClassifier(vocab_size,64,32,len(classes)).to(device)\n",
|
||||
"train_epoch_emb(net,train_loader_len, lr=0.001,use_pack_sequence=True)\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"> **注意:** 您可能已經注意到我們傳遞給訓練函數的參數 `use_pack_sequence`。目前,`pack_padded_sequence` 函數要求長度序列張量位於 CPU 設備上,因此訓練函數需要避免在訓練時將長度序列數據移動到 GPU。您可以查看 [`torchnlp.py`](../../../../../lessons/5-NLP/16-RNN/torchnlp.py) 文件中 `train_emb` 函數的實現。\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## 雙向和多層 RNN\n",
|
||||
"\n",
|
||||
"在我們的例子中,所有的循環神經網絡都是單向運行的,從序列的開頭到結尾。這看起來很自然,因為它類似於我們閱讀和聆聽語音的方式。然而,在許多實際情況下,我們可以隨機訪問輸入序列,因此在兩個方向上運行循環計算可能更有意義。這樣的網絡被稱為 **雙向** RNN,可以通過在 RNN/LSTM/GRU 構造函數中傳遞參數 `bidirectional=True` 來創建。\n",
|
||||
"\n",
|
||||
"處理雙向網絡時,我們需要兩個隱藏狀態向量,每個方向一個。PyTorch 將這些向量編碼為一個大小是原來兩倍的向量,這非常方便,因為通常我們會將生成的隱藏狀態傳遞給全連接線性層,只需在創建該層時考慮到這個大小的增加即可。\n",
|
||||
"\n",
|
||||
"無論是單向還是雙向的循環網絡,都能在序列中捕捉某些模式,並將其存儲到狀態向量中或傳遞到輸出中。與卷積網絡類似,我們可以在第一層之上構建另一個循環層,以捕捉更高層次的模式,這些模式是由第一層提取的低層次模式構建而成的。這引出了 **多層 RNN** 的概念,它由兩層或更多的循環網絡組成,其中前一層的輸出作為下一層的輸入。\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"*圖片來源於 [這篇精彩的文章](https://towardsdatascience.com/from-a-lstm-cell-to-a-multilayer-lstm-network-with-pytorch-2899eb5696f3) 作者 Fernando López*\n",
|
||||
"\n",
|
||||
"PyTorch 讓構建這樣的網絡變得非常簡單,因為只需在 RNN/LSTM/GRU 構造函數中傳遞參數 `num_layers`,即可自動構建多層循環網絡。這也意味著隱藏/狀態向量的大小會按比例增加,處理循環層的輸出時需要考慮到這一點。\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## RNNs 用於其他任務\n",
|
||||
"\n",
|
||||
"在本單元中,我們已經看到 RNNs 可以用於序列分類,但事實上,它們還能處理更多任務,例如文本生成、機器翻譯等等。我們會在下一單元探討這些任務。\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"\n---\n\n**免責聲明**: \n本文件已使用人工智能翻譯服務 [Co-op Translator](https://github.com/Azure/co-op-translator) 進行翻譯。儘管我們致力於提供準確的翻譯,但請注意,自動翻譯可能包含錯誤或不準確之處。原始語言的文件應被視為權威來源。對於重要資訊,建議使用專業人工翻譯。我們對因使用此翻譯而引起的任何誤解或錯誤解釋概不負責。\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"interpreter": {
|
||||
"hash": "16af2a8bbb083ea23e5e41c7f5787656b2ce26968575d8763f2c4b17f9cd711f"
|
||||
},
|
||||
"kernelspec": {
|
||||
"display_name": "Python 3.8.12 ('py38')",
|
||||
"language": "python",
|
||||
"name": "python3"
|
||||
},
|
||||
"language_info": {
|
||||
"codemirror_mode": {
|
||||
"name": "ipython",
|
||||
"version": 3
|
||||
},
|
||||
"file_extension": ".py",
|
||||
"mimetype": "text/x-python",
|
||||
"name": "python",
|
||||
"nbconvert_exporter": "python",
|
||||
"pygments_lexer": "ipython3",
|
||||
"version": "3.8.12"
|
||||
},
|
||||
"coopTranslator": {
|
||||
"original_hash": "522ee52ae3d5ae933e283286254e9a55",
|
||||
"translation_date": "2025-08-31T10:50:36+00:00",
|
||||
"source_file": "lessons/5-NLP/16-RNN/RNNPyTorch.ipynb",
|
||||
"language_code": "hk"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 2
|
||||
}
|
||||
|
|
@ -0,0 +1,460 @@
|
|||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"# 循環神經網絡\n",
|
||||
"\n",
|
||||
"在上一個模組中,我們探討了文本的豐富語義表示。我們使用的架構能夠捕捉句子中詞語的聚合意義,但它並未考慮詞語的**順序**,因為嵌入後的聚合操作會移除原始文本中的這些信息。由於這些模型無法表示詞語的排列順序,因此它們無法解決更複雜或更具歧義的任務,例如文本生成或問題回答。\n",
|
||||
"\n",
|
||||
"為了捕捉文本序列的意義,我們將使用一種名為**循環神經網絡**(Recurrent Neural Network,簡稱 RNN)的神經網絡架構。在使用 RNN 時,我們會將句子逐個標記傳遞給網絡,網絡會生成某種**狀態**,然後我們將該狀態與下一個標記一起再次傳遞給網絡。\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"給定標記的輸入序列 $X_0,\\dots,X_n$,RNN 會創建一系列神經網絡塊,並通過反向傳播對這個序列進行端到端的訓練。每個網絡塊以 $(X_i,S_i)$ 作為輸入,並生成 $S_{i+1}$ 作為結果。最終狀態 $S_n$ 或輸出 $Y_n$ 會進入線性分類器以生成結果。所有網絡塊共享相同的權重,並通過一次反向傳播進行端到端訓練。\n",
|
||||
"\n",
|
||||
"> 上圖展示了循環神經網絡的展開形式(左側)和更緊湊的循環表示形式(右側)。需要注意的是,所有 RNN 單元都具有相同的**可共享權重**。\n",
|
||||
"\n",
|
||||
"由於狀態向量 $S_0,\\dots,S_n$ 是通過網絡傳遞的,RNN 能夠學習詞語之間的順序依賴。例如,當某個序列中出現詞語 *not* 時,它可以學習在狀態向量中否定某些元素。\n",
|
||||
"\n",
|
||||
"在內部,每個 RNN 單元包含兩個權重矩陣:$W_H$ 和 $W_I$,以及偏置 $b$。在每個 RNN 步驟中,給定輸入 $X_i$ 和輸入狀態 $S_i$,輸出狀態的計算公式為 $S_{i+1} = f(W_H\\times S_i + W_I\\times X_i+b)$,其中 $f$ 是激活函數(通常為 $\\tanh$)。\n",
|
||||
"\n",
|
||||
"> 對於像文本生成(我們會在下一單元中探討)或機器翻譯這類問題,我們還希望在每個 RNN 步驟中獲得某些輸出值。在這種情況下,還會有另一個矩陣 $W_O$,輸出值的計算公式為 $Y_i=f(W_O\\times S_i+b_O)$。\n",
|
||||
"\n",
|
||||
"現在讓我們看看循環神經網絡如何幫助我們對新聞數據集進行分類。\n",
|
||||
"\n",
|
||||
"> 在沙盒環境中,我們需要運行以下單元格以確保所需的庫已安裝並且數據已預取。如果您在本地運行,可以跳過以下單元格。\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 1,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import sys\n",
|
||||
"!{sys.executable} -m pip install --quiet tensorflow_datasets==4.4.0\n",
|
||||
"!cd ~ && wget -q -O - https://mslearntensorflowlp.blob.core.windows.net/data/tfds-ag-news.tgz | tar xz"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 2,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import tensorflow as tf\n",
|
||||
"from tensorflow import keras\n",
|
||||
"import tensorflow_datasets as tfds\n",
|
||||
"import numpy as np\n",
|
||||
"\n",
|
||||
"# We are going to be training pretty large models. In order not to face errors, we need\n",
|
||||
"# to set tensorflow option to grow GPU memory allocation when required\n",
|
||||
"physical_devices = tf.config.list_physical_devices('GPU') \n",
|
||||
"if len(physical_devices)>0:\n",
|
||||
" tf.config.experimental.set_memory_growth(physical_devices[0], True)\n",
|
||||
"\n",
|
||||
"ds_train, ds_test = tfds.load('ag_news_subset').values()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"nteract": {
|
||||
"transient": {
|
||||
"deleting": false
|
||||
}
|
||||
}
|
||||
},
|
||||
"source": [
|
||||
"在訓練大型模型時,GPU 記憶體分配可能會成為一個問題。我們可能需要嘗試不同的迷你批次大小,以確保數據能夠適配 GPU 記憶體,同時訓練速度也足夠快。如果你在自己的 GPU 機器上運行此代碼,可以嘗試調整迷你批次大小來加快訓練速度。\n",
|
||||
"\n",
|
||||
"> **Note**: 某些版本的 NVidia 驅動程式已知在訓練模型後不會釋放記憶體。我們在這個筆記本中運行了幾個範例,這可能會導致某些配置下記憶體耗盡,特別是當你在同一個筆記本中進行自己的實驗時。如果在開始訓練模型時遇到一些奇怪的錯誤,你可能需要重新啟動筆記本的內核。\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 3,
|
||||
"metadata": {
|
||||
"collapsed": true,
|
||||
"jupyter": {
|
||||
"outputs_hidden": false,
|
||||
"source_hidden": false
|
||||
},
|
||||
"nteract": {
|
||||
"transient": {
|
||||
"deleting": false
|
||||
}
|
||||
}
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"batch_size = 16\n",
|
||||
"embed_size = 64"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## 簡單 RNN 分類器\n",
|
||||
"\n",
|
||||
"在簡單 RNN 的情況下,每個循環單元都是一個簡單的線性網絡,它接收輸入向量和狀態向量,並生成新的狀態向量。在 Keras 中,這可以用 `SimpleRNN` 層來表示。\n",
|
||||
"\n",
|
||||
"雖然我們可以直接將 one-hot 編碼的標記傳遞給 RNN 層,但這並不是一個好主意,因為它們的維度太高。因此,我們會使用嵌入層來降低詞向量的維度,接著是 RNN 層,最後是一個 `Dense` 分類器。\n",
|
||||
"\n",
|
||||
"> **注意**:在維度不是很高的情況下,例如使用字符級標記化時,直接將 one-hot 編碼的標記傳遞給 RNN 單元可能是合理的。\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 4,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"Model: \"sequential\"\n",
|
||||
"_________________________________________________________________\n",
|
||||
"Layer (type) Output Shape Param # \n",
|
||||
"=================================================================\n",
|
||||
"text_vectorization (TextVect (None, None) 0 \n",
|
||||
"_________________________________________________________________\n",
|
||||
"embedding (Embedding) (None, None, 64) 1280000 \n",
|
||||
"_________________________________________________________________\n",
|
||||
"simple_rnn (SimpleRNN) (None, 16) 1296 \n",
|
||||
"_________________________________________________________________\n",
|
||||
"dense (Dense) (None, 4) 68 \n",
|
||||
"=================================================================\n",
|
||||
"Total params: 1,281,364\n",
|
||||
"Trainable params: 1,281,364\n",
|
||||
"Non-trainable params: 0\n",
|
||||
"_________________________________________________________________\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"vocab_size = 20000\n",
|
||||
"\n",
|
||||
"vectorizer = keras.layers.experimental.preprocessing.TextVectorization(\n",
|
||||
" max_tokens=vocab_size,\n",
|
||||
" input_shape=(1,))\n",
|
||||
"\n",
|
||||
"model = keras.models.Sequential([\n",
|
||||
" vectorizer,\n",
|
||||
" keras.layers.Embedding(vocab_size, embed_size),\n",
|
||||
" keras.layers.SimpleRNN(16),\n",
|
||||
" keras.layers.Dense(4,activation='softmax')\n",
|
||||
"])\n",
|
||||
"\n",
|
||||
"model.summary()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"> **注意:** 我們在這裡使用未經訓練的嵌入層以簡化操作,但為了獲得更好的效果,我們可以使用 Word2Vec 預訓練的嵌入層,如前一單元所述。這會是一個很好的練習,讓你嘗試將此程式碼改為使用預訓練的嵌入層。\n",
|
||||
"\n",
|
||||
"現在讓我們開始訓練 RNN。一般來說,RNN 的訓練相當困難,因為當 RNN 單元沿著序列長度展開時,反向傳播所涉及的層數會非常多。因此,我們需要選擇較小的學習率,並在更大的數據集上訓練網絡以獲得良好的結果。這可能需要相當長的時間,因此建議使用 GPU。\n",
|
||||
"\n",
|
||||
"為了加快速度,我們將僅使用新聞標題來訓練 RNN 模型,省略描述部分。你可以嘗試加入描述進行訓練,看看是否能讓模型成功訓練。\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 5,
|
||||
"metadata": {
|
||||
"scrolled": true
|
||||
},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"Training vectorizer\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"def extract_title(x):\n",
|
||||
" return x['title']\n",
|
||||
"\n",
|
||||
"def tupelize_title(x):\n",
|
||||
" return (extract_title(x),x['label'])\n",
|
||||
"\n",
|
||||
"print('Training vectorizer')\n",
|
||||
"vectorizer.adapt(ds_train.take(2000).map(extract_title))"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 6,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"7500/7500 [==============================] - 82s 11ms/step - loss: 0.6629 - acc: 0.7623 - val_loss: 0.5559 - val_acc: 0.7995\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"<tensorflow.python.keras.callbacks.History at 0x7f3e0030d350>"
|
||||
]
|
||||
},
|
||||
"execution_count": 6,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"model.compile(loss='sparse_categorical_crossentropy',metrics=['acc'], optimizer='adam')\n",
|
||||
"model.fit(ds_train.map(tupelize_title).batch(batch_size),validation_data=ds_test.map(tupelize_title).batch(batch_size))"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"nteract": {
|
||||
"transient": {
|
||||
"deleting": false
|
||||
}
|
||||
}
|
||||
},
|
||||
"source": [
|
||||
"> **注意**,由於我們僅針對新聞標題進行訓練,因此準確性可能會較低。\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## 重溫變數序列\n",
|
||||
"\n",
|
||||
"記住,`TextVectorization` 層會自動在小批量中用填充標記來填充可變長度的序列。然而,這些填充標記也會參與訓練,並可能使模型的收斂變得更複雜。\n",
|
||||
"\n",
|
||||
"我們可以採取幾種方法來減少填充的數量。其中一種方法是根據序列長度重新排列數據集,並按大小分組所有序列。這可以使用 `tf.data.experimental.bucket_by_sequence_length` 函數來完成(請參閱[文件](https://www.tensorflow.org/api_docs/python/tf/data/experimental/bucket_by_sequence_length))。\n",
|
||||
"\n",
|
||||
"另一種方法是使用**遮罩**。在 Keras 中,某些層支持額外的輸入,該輸入顯示哪些標記應在訓練時被考慮。要將遮罩整合到我們的模型中,我們可以選擇添加一個單獨的 `Masking` 層([文件](https://keras.io/api/layers/core_layers/masking/)),或者在我們的 `Embedding` 層中指定 `mask_zero=True` 參數。\n",
|
||||
"\n",
|
||||
"> **Note**: 完成整個數據集的一個訓練週期大約需要 5 分鐘。如果你失去耐心,可以隨時中斷訓練。你也可以通過在 `ds_train` 和 `ds_test` 數據集後添加 `.take(...)` 子句來限制用於訓練的數據量。\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 7,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"7500/7500 [==============================] - 371s 49ms/step - loss: 0.5401 - acc: 0.8079 - val_loss: 0.3780 - val_acc: 0.8822\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"<tensorflow.python.keras.callbacks.History at 0x7f3dec118850>"
|
||||
]
|
||||
},
|
||||
"execution_count": 7,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"def extract_text(x):\n",
|
||||
" return x['title']+' '+x['description']\n",
|
||||
"\n",
|
||||
"def tupelize(x):\n",
|
||||
" return (extract_text(x),x['label'])\n",
|
||||
"\n",
|
||||
"model = keras.models.Sequential([\n",
|
||||
" vectorizer,\n",
|
||||
" keras.layers.Embedding(vocab_size,embed_size,mask_zero=True),\n",
|
||||
" keras.layers.SimpleRNN(16),\n",
|
||||
" keras.layers.Dense(4,activation='softmax')\n",
|
||||
"])\n",
|
||||
"\n",
|
||||
"model.compile(loss='sparse_categorical_crossentropy',metrics=['acc'], optimizer='adam')\n",
|
||||
"model.fit(ds_train.map(tupelize).batch(batch_size),validation_data=ds_test.map(tupelize).batch(batch_size))"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"現在我們使用遮罩技術,可以在整個標題和描述的數據集上訓練模型。\n",
|
||||
"\n",
|
||||
"> **注意**:你有沒有注意到,我們一直在使用基於新聞標題訓練的向量化工具,而不是整篇文章的內容?這可能會導致某些詞元被忽略,因此重新訓練向量化工具會更好。不過,這可能只會帶來非常小的影響,所以為了簡化流程,我們會繼續使用之前預訓練的向量化工具。\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## LSTM:長短期記憶\n",
|
||||
"\n",
|
||||
"RNNs 的主要問題之一是 **梯度消失**。RNNs 可以非常長,在反向傳播過程中可能難以將梯度傳遞回網絡的第一層。當出現這種情況時,網絡無法學習遠距離的標記之間的關係。解決這個問題的一種方法是通過使用 **閘門** 引入 **顯式狀態管理**。最常見的引入閘門的架構是 **長短期記憶**(LSTM)和 **門控循環單元**(GRU)。我們在這裡將討論 LSTM。\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"LSTM 網絡的組織方式與 RNN 類似,但有兩個狀態會從層到層傳遞:實際狀態 $c$ 和隱藏向量 $h$。在每個單元中,隱藏向量 $h_{t-1}$ 與輸入 $x_t$ 結合,並共同控制狀態 $c_t$ 和輸出 $h_{t}$ 的變化,這是通過 **閘門** 完成的。每個閘門都有 sigmoid 激活函數(輸出範圍為 $[0,1]$),可以被視為在與狀態向量相乘時的位掩碼。LSTM 包含以下閘門(如上圖所示,從左到右):\n",
|
||||
"* **遺忘閘門**:決定向量 $c_{t-1}$ 的哪些部分需要遺忘,哪些需要保留。\n",
|
||||
"* **輸入閘門**:決定來自輸入向量和前一隱藏向量的信息有多少應該被整合到狀態向量中。\n",
|
||||
"* **輸出閘門**:接收新的狀態向量並決定其哪些部分將用於生成新的隱藏向量 $h_t$。\n",
|
||||
"\n",
|
||||
"狀態 $c$ 的組件可以被視為可以開啟或關閉的標誌。例如,當我們在序列中遇到名字 *Alice* 時,我們猜測它指的是一位女性,並在狀態中啟動表示句子中有女性名詞的標誌。當我們進一步遇到 *and Tom* 這些詞時,我們會啟動表示句子中有複數名詞的標誌。因此,通過操控狀態,我們可以追蹤句子的語法屬性。\n",
|
||||
"\n",
|
||||
"> **注意**:這裡有一個很棒的資源可以幫助理解 LSTM 的內部結構:[理解 LSTM 網絡](https://colah.github.io/posts/2015-08-Understanding-LSTMs/) 作者是 Christopher Olah。\n",
|
||||
"\n",
|
||||
"雖然 LSTM 單元的內部結構看起來可能很複雜,但 Keras 將這些實現隱藏在 `LSTM` 層中,因此在上面的示例中,我們唯一需要做的就是替換循環層:\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 8,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"15000/15000 [==============================] - 188s 13ms/step - loss: 0.5692 - acc: 0.7916 - val_loss: 0.3441 - val_acc: 0.8870\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"<tensorflow.python.keras.callbacks.History at 0x7f3d6af5c350>"
|
||||
]
|
||||
},
|
||||
"execution_count": 8,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"model = keras.models.Sequential([\n",
|
||||
" vectorizer,\n",
|
||||
" keras.layers.Embedding(vocab_size, embed_size),\n",
|
||||
" keras.layers.LSTM(8),\n",
|
||||
" keras.layers.Dense(4,activation='softmax')\n",
|
||||
"])\n",
|
||||
"\n",
|
||||
"model.compile(loss='sparse_categorical_crossentropy',metrics=['acc'], optimizer='adam')\n",
|
||||
"model.fit(ds_train.map(tupelize).batch(8),validation_data=ds_test.map(tupelize).batch(8))"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": []
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## 雙向和多層 RNN\n",
|
||||
"\n",
|
||||
"在我們之前的例子中,循環神經網絡(RNN)都是從序列的開頭運行到結尾。這對我們來說很自然,因為它符合我們閱讀或聆聽語音的方向。然而,對於需要隨機訪問輸入序列的情境來說,讓循環計算在兩個方向上運行會更有意義。允許雙向計算的 RNN 被稱為 **雙向 RNN**,可以通過將循環層包裹在一個特殊的 `Bidirectional` 層中來創建。\n",
|
||||
"\n",
|
||||
"> **Note**: `Bidirectional` 層會在內部生成該層的兩個副本,並將其中一個副本的 `go_backwards` 屬性設置為 `True`,使其沿著序列的相反方向運行。\n",
|
||||
"\n",
|
||||
"無論是單向還是雙向的循環網絡,都能捕捉序列中的模式,並將其存儲到狀態向量中或作為輸出返回。與卷積網絡類似,我們可以在第一個循環層之後再構建另一個循環層,以捕捉更高層次的模式,這些模式是由第一層提取的低層次模式構建而成的。這引出了 **多層 RNN** 的概念,它由兩層或更多層循環網絡組成,其中前一層的輸出作為下一層的輸入。\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"*圖片來源於 [這篇精彩的文章](https://towardsdatascience.com/from-a-lstm-cell-to-a-multilayer-lstm-network-with-pytorch-2899eb5696f3),作者是 Fernando López。*\n",
|
||||
"\n",
|
||||
"Keras 讓構建這些網絡變得非常簡單,因為你只需要在模型中添加更多的循環層。對於最後一層以外的所有層,我們需要指定 `return_sequences=True` 參數,因為我們需要該層返回所有的中間狀態,而不僅僅是循環計算的最終狀態。\n",
|
||||
"\n",
|
||||
"現在,我們來為分類問題構建一個雙層雙向 LSTM。\n",
|
||||
"\n",
|
||||
"> **Note** 這段代碼執行起來可能需要較長的時間,但它能給我們帶來目前為止最高的準確率。所以也許值得等待並看看結果。\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 9,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"5044/7500 [===================>..........] - ETA: 2:33 - loss: 0.3709 - acc: 0.8706\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\r5045/7500 [===================>..........] - ETA: 2:33 - loss: 0.3709 - acc: 0.8706"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"model = keras.models.Sequential([\n",
|
||||
" vectorizer,\n",
|
||||
" keras.layers.Embedding(vocab_size, 128, mask_zero=True),\n",
|
||||
" keras.layers.Bidirectional(keras.layers.LSTM(64,return_sequences=True)),\n",
|
||||
" keras.layers.Bidirectional(keras.layers.LSTM(64)), \n",
|
||||
" keras.layers.Dense(4,activation='softmax')\n",
|
||||
"])\n",
|
||||
"\n",
|
||||
"model.compile(loss='sparse_categorical_crossentropy',metrics=['acc'], optimizer='adam')\n",
|
||||
"model.fit(ds_train.map(tupelize).batch(batch_size),\n",
|
||||
" validation_data=ds_test.map(tupelize).batch(batch_size))"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## RNNs 用於其他任務\n",
|
||||
"\n",
|
||||
"到目前為止,我們主要集中在使用 RNNs 來對文本序列進行分類。但它們還能處理更多任務,例如文本生成和機器翻譯——我們會在下一單元探討這些任務。\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"\n---\n\n**免責聲明**: \n本文件已使用人工智能翻譯服務 [Co-op Translator](https://github.com/Azure/co-op-translator) 進行翻譯。我們致力於提供準確的翻譯,但請注意,自動翻譯可能包含錯誤或不準確之處。應以原文文件作為權威來源。對於關鍵資訊,建議尋求專業人工翻譯。我們對因使用此翻譯而引起的任何誤解或誤釋不承擔責任。\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"kernel_info": {
|
||||
"name": "conda-env-py37_tensorflow-py"
|
||||
},
|
||||
"kernelspec": {
|
||||
"display_name": "py37_tensorflow",
|
||||
"language": "python",
|
||||
"name": "conda-env-py37_tensorflow-py"
|
||||
},
|
||||
"language_info": {
|
||||
"codemirror_mode": {
|
||||
"name": "ipython",
|
||||
"version": 3
|
||||
},
|
||||
"file_extension": ".py",
|
||||
"mimetype": "text/x-python",
|
||||
"name": "python",
|
||||
"nbconvert_exporter": "python",
|
||||
"pygments_lexer": "ipython3",
|
||||
"version": "3.7.9"
|
||||
},
|
||||
"nteract": {
|
||||
"version": "nteract-front-end@1.0.0"
|
||||
},
|
||||
"coopTranslator": {
|
||||
"original_hash": "81351e61f619b432ff51010a4f993194",
|
||||
"translation_date": "2025-08-31T10:48:08+00:00",
|
||||
"source_file": "lessons/5-NLP/16-RNN/RNNTF.ipynb",
|
||||
"language_code": "hk"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 4
|
||||
}
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
# 作業:筆記本
|
||||
|
||||
使用與本課程相關的筆記本(無論是 PyTorch 或 TensorFlow 版本),使用您自己的數據集重新執行,或許可以使用 Kaggle 上的數據集,並註明來源。重寫筆記本以強調您自己的發現。嘗試使用不同類型的數據集並記錄您的發現,例如使用像 [這個關於天氣推文的 Kaggle 比賽數據集](https://www.kaggle.com/competitions/crowdflower-weather-twitter/data?select=train.csv) 的文本。
|
||||
|
||||
**免責聲明**:
|
||||
本文件使用人工智能翻譯服務 [Co-op Translator](https://github.com/Azure/co-op-translator) 進行翻譯。我們致力於提供準確的翻譯,但請注意,自動翻譯可能包含錯誤或不準確之處。應以原文文件作為權威來源。對於關鍵資訊,建議尋求專業人工翻譯。我們對因使用此翻譯而引起的任何誤解或誤釋不承擔責任。
|
||||
|
|
@ -0,0 +1,414 @@
|
|||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"# 生成式網絡\n",
|
||||
"\n",
|
||||
"循環神經網絡(Recurrent Neural Networks, RNNs)及其門控單元變體,例如長短期記憶單元(Long Short Term Memory Cells, LSTMs)和門控循環單元(Gated Recurrent Units, GRUs),提供了一種語言建模的機制,也就是說,它們可以學習單詞的排列順序,並對序列中的下一個單詞進行預測。這使得我們可以使用 RNNs 來完成**生成任務**,例如普通文本生成、機器翻譯,甚至是圖像描述生成。\n",
|
||||
"\n",
|
||||
"在上一單元中討論的 RNN 結構中,每個 RNN 單元會輸出下一個隱藏狀態。然而,我們也可以為每個循環單元添加另一個輸出,這樣就可以輸出一個**序列**(其長度與原始序列相等)。此外,我們還可以使用不在每一步接受輸入的 RNN 單元,而是僅接受一個初始狀態向量,然後生成一個輸出序列。\n",
|
||||
"\n",
|
||||
"在這個筆記本中,我們將專注於幫助我們生成文本的簡單生成模型。為了簡化,我們將構建一個**字符級網絡**,逐字母生成文本。在訓練過程中,我們需要使用一些文本語料庫,並將其拆分為字母序列。\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 1,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"Loading dataset...\n",
|
||||
"Building vocab...\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"import torch\n",
|
||||
"import torchtext\n",
|
||||
"import numpy as np\n",
|
||||
"from torchnlp import *\n",
|
||||
"train_dataset,test_dataset,classes,vocab = load_dataset()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## 建立字符詞彙表\n",
|
||||
"\n",
|
||||
"為了建立基於字符層級的生成網絡,我們需要將文本拆分為單個字符,而不是單詞。這可以通過定義一個不同的分詞器來完成:\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 2,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"Vocabulary size = 82\n",
|
||||
"Encoding of 'a' is 1\n",
|
||||
"Character with code 13 is c\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"def char_tokenizer(words):\n",
|
||||
" return list(words) #[word for word in words]\n",
|
||||
"\n",
|
||||
"counter = collections.Counter()\n",
|
||||
"for (label, line) in train_dataset:\n",
|
||||
" counter.update(char_tokenizer(line))\n",
|
||||
"vocab = torchtext.vocab.vocab(counter)\n",
|
||||
"\n",
|
||||
"vocab_size = len(vocab)\n",
|
||||
"print(f\"Vocabulary size = {vocab_size}\")\n",
|
||||
"print(f\"Encoding of 'a' is {vocab.get_stoi()['a']}\")\n",
|
||||
"print(f\"Character with code 13 is {vocab.get_itos()[13]}\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"讓我們看看如何對我們數據集中的文本進行編碼的例子:\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 3,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"tensor([ 0, 1, 2, 2, 3, 4, 5, 6, 3, 7, 8, 1, 9, 10, 3, 11, 2, 1,\n",
|
||||
" 12, 3, 7, 1, 13, 14, 3, 15, 16, 5, 17, 3, 5, 18, 8, 3, 7, 2,\n",
|
||||
" 1, 13, 14, 3, 19, 20, 8, 21, 5, 8, 9, 10, 22, 3, 20, 8, 21, 5,\n",
|
||||
" 8, 9, 10, 3, 23, 3, 4, 18, 17, 9, 5, 23, 10, 8, 2, 2, 8, 9,\n",
|
||||
" 10, 24, 3, 0, 1, 2, 2, 3, 4, 5, 9, 8, 8, 5, 25, 10, 3, 26,\n",
|
||||
" 12, 27, 16, 26, 2, 27, 16, 28, 29, 30, 1, 16, 26, 3, 17, 31, 3, 21,\n",
|
||||
" 2, 5, 9, 1, 23, 13, 32, 16, 27, 13, 10, 24, 3, 1, 9, 8, 3, 10,\n",
|
||||
" 8, 8, 27, 16, 28, 3, 28, 9, 8, 8, 16, 3, 1, 28, 1, 27, 16, 6])"
|
||||
]
|
||||
},
|
||||
"execution_count": 3,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"def enc(x):\n",
|
||||
" return torch.LongTensor(encode(x,voc=vocab,tokenizer=char_tokenizer))\n",
|
||||
"\n",
|
||||
"enc(train_dataset[0][1])"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## 訓練生成式 RNN\n",
|
||||
"\n",
|
||||
"我們訓練 RNN 生成文本的方法如下。在每一步中,我們會取一段長度為 `nchars` 的字符序列,並要求網絡為每個輸入字符生成下一個輸出字符:\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"根據實際情況,我們可能還需要加入一些特殊字符,例如 *序列結束符* `<eos>`。在我們的情況下,我們只希望訓練網絡進行無限文本生成,因此我們會將每段序列的大小固定為 `nchars` 個標記。因此,每個訓練樣本將包含 `nchars` 個輸入和 `nchars` 個輸出(輸入序列向左移動一個符號)。小批量訓練將由多個這樣的序列組成。\n",
|
||||
"\n",
|
||||
"我們生成小批量的方法是取每段長度為 `l` 的新聞文本,並從中生成所有可能的輸入-輸出組合(這樣的組合會有 `l-nchars` 個)。它們將形成一個小批量,而每次訓練步驟的小批量大小會有所不同。\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 4,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"(tensor([[ 0, 1, 2, ..., 28, 29, 30],\n",
|
||||
" [ 1, 2, 2, ..., 29, 30, 1],\n",
|
||||
" [ 2, 2, 3, ..., 30, 1, 16],\n",
|
||||
" ...,\n",
|
||||
" [20, 8, 21, ..., 1, 28, 1],\n",
|
||||
" [ 8, 21, 5, ..., 28, 1, 27],\n",
|
||||
" [21, 5, 8, ..., 1, 27, 16]]),\n",
|
||||
" tensor([[ 1, 2, 2, ..., 29, 30, 1],\n",
|
||||
" [ 2, 2, 3, ..., 30, 1, 16],\n",
|
||||
" [ 2, 3, 4, ..., 1, 16, 26],\n",
|
||||
" ...,\n",
|
||||
" [ 8, 21, 5, ..., 28, 1, 27],\n",
|
||||
" [21, 5, 8, ..., 1, 27, 16],\n",
|
||||
" [ 5, 8, 9, ..., 27, 16, 6]]))"
|
||||
]
|
||||
},
|
||||
"execution_count": 4,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"nchars = 100\n",
|
||||
"\n",
|
||||
"def get_batch(s,nchars=nchars):\n",
|
||||
" ins = torch.zeros(len(s)-nchars,nchars,dtype=torch.long,device=device)\n",
|
||||
" outs = torch.zeros(len(s)-nchars,nchars,dtype=torch.long,device=device)\n",
|
||||
" for i in range(len(s)-nchars):\n",
|
||||
" ins[i] = enc(s[i:i+nchars])\n",
|
||||
" outs[i] = enc(s[i+1:i+nchars+1])\n",
|
||||
" return ins,outs\n",
|
||||
"\n",
|
||||
"get_batch(train_dataset[0][1])"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"現在讓我們定義生成器網絡。它可以基於我們在上一單元討論過的任何循環單元(簡單、LSTM 或 GRU)。在我們的例子中,我們將使用 LSTM。\n",
|
||||
"\n",
|
||||
"由於網絡以字符作為輸入,而詞彙量相對較小,我們不需要嵌入層,直接使用獨熱編碼的輸入即可進入 LSTM 單元。然而,因為我們以字符編號作為輸入,所以在傳遞給 LSTM 之前需要對它們進行獨熱編碼。這可以在 `forward` 傳遞過程中通過調用 `one_hot` 函數來完成。輸出編碼器將是一個線性層,用於將隱藏狀態轉換為獨熱編碼的輸出。\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 5,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"class LSTMGenerator(torch.nn.Module):\n",
|
||||
" def __init__(self, vocab_size, hidden_dim):\n",
|
||||
" super().__init__()\n",
|
||||
" self.rnn = torch.nn.LSTM(vocab_size,hidden_dim,batch_first=True)\n",
|
||||
" self.fc = torch.nn.Linear(hidden_dim, vocab_size)\n",
|
||||
"\n",
|
||||
" def forward(self, x, s=None):\n",
|
||||
" x = torch.nn.functional.one_hot(x,vocab_size).to(torch.float32)\n",
|
||||
" x,s = self.rnn(x,s)\n",
|
||||
" return self.fc(x),s"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"在訓練過程中,我們希望能夠對生成的文本進行取樣。為此,我們將定義一個 `generate` 函數,該函數會生成一個長度為 `size` 的輸出字串,並以初始字串 `start` 開始。\n",
|
||||
"\n",
|
||||
"其運作方式如下:首先,我們會將整個初始字串傳遞到網絡中,並獲取輸出狀態 `s` 和下一個預測字符 `out`。由於 `out` 是以 one-hot 編碼表示的,我們使用 `argmax` 來獲取該字符在詞彙表中的索引 `nc`,然後使用 `itos` 找出實際字符,並將其附加到結果字符列表 `chars` 中。這個生成一個字符的過程會重複執行 `size` 次,以生成所需數量的字符。\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 8,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"def generate(net,size=100,start='today '):\n",
|
||||
" chars = list(start)\n",
|
||||
" out, s = net(enc(chars).view(1,-1).to(device))\n",
|
||||
" for i in range(size):\n",
|
||||
" nc = torch.argmax(out[0][-1])\n",
|
||||
" chars.append(vocab.get_itos()[nc])\n",
|
||||
" out, s = net(nc.view(1,-1),s)\n",
|
||||
" return ''.join(chars)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"現在開始訓練吧!訓練循環幾乎與之前的例子相同,但這次我們每隔 1000 個 epoch 就會打印一次生成的文本,而不是準確率。\n",
|
||||
"\n",
|
||||
"需要特別注意的是計算損失的方式。我們需要根據 one-hot 編碼的輸出 `out` 和期望的文本 `text_out`(即字符索引的列表)來計算損失。幸運的是,`cross_entropy` 函數的第一個參數是未正規化的網絡輸出,第二個參數是類別編號,這正好符合我們的需求。它還會自動對小批量的大小進行平均。\n",
|
||||
"\n",
|
||||
"我們還通過 `samples_to_train` 限制訓練樣本的數量,以避免等待過久。我們鼓勵你進行實驗,嘗試更長時間的訓練,可能是多個 epoch(在這種情況下,你需要在這段代碼外再創建一個循環)。\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 9,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"Current loss = 4.398899078369141\n",
|
||||
"today sr sr sr sr sr sr sr sr sr sr sr sr sr sr sr sr sr sr sr sr sr sr sr sr sr sr sr sr sr sr sr sr sr s\n",
|
||||
"Current loss = 2.161320447921753\n",
|
||||
"today and to the tor to to the tor to to the tor to to the tor to to the tor to to the tor to to the tor t\n",
|
||||
"Current loss = 1.6722588539123535\n",
|
||||
"today and the court to the could to the could to the could to the could to the could to the could to the c\n",
|
||||
"Current loss = 2.423795223236084\n",
|
||||
"today and a second to the conternation of the conternation of the conternation of the conternation of the \n",
|
||||
"Current loss = 1.702607274055481\n",
|
||||
"today and the company to the company to the company to the company to the company to the company to the co\n",
|
||||
"Current loss = 1.692358136177063\n",
|
||||
"today and the company to the company to the company to the company to the company to the company to the co\n",
|
||||
"Current loss = 1.9722288846969604\n",
|
||||
"today and the control the control the control the control the control the control the control the control \n",
|
||||
"Current loss = 1.8705692291259766\n",
|
||||
"today and the second to the second to the second to the second to the second to the second to the second t\n",
|
||||
"Current loss = 1.7626899480819702\n",
|
||||
"today and a security and a security and a security and a security and a security and a security and a secu\n",
|
||||
"Current loss = 1.5574463605880737\n",
|
||||
"today and the company and the company and the company and the company and the company and the company and \n",
|
||||
"Current loss = 1.5620026588439941\n",
|
||||
"today and the be that the be the be that the be the be that the be the be that the be the be that the be t\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"net = LSTMGenerator(vocab_size,64).to(device)\n",
|
||||
"\n",
|
||||
"samples_to_train = 10000\n",
|
||||
"optimizer = torch.optim.Adam(net.parameters(),0.01)\n",
|
||||
"loss_fn = torch.nn.CrossEntropyLoss()\n",
|
||||
"net.train()\n",
|
||||
"for i,x in enumerate(train_dataset):\n",
|
||||
" # x[0] is class label, x[1] is text\n",
|
||||
" if len(x[1])-nchars<10:\n",
|
||||
" continue\n",
|
||||
" samples_to_train-=1\n",
|
||||
" if not samples_to_train: break\n",
|
||||
" text_in, text_out = get_batch(x[1])\n",
|
||||
" optimizer.zero_grad()\n",
|
||||
" out,s = net(text_in)\n",
|
||||
" loss = torch.nn.functional.cross_entropy(out.view(-1,vocab_size),text_out.flatten()) #cross_entropy(out,labels)\n",
|
||||
" loss.backward()\n",
|
||||
" optimizer.step()\n",
|
||||
" if i%1000==0:\n",
|
||||
" print(f\"Current loss = {loss.item()}\")\n",
|
||||
" print(generate(net))"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"這個例子已經生成了一些相當不錯的文本,但仍有幾個方面可以進一步改進:\n",
|
||||
"\n",
|
||||
"* **更好的小批次生成**。我們用來準備訓練數據的方法是從一個樣本中生成一個小批次。這並不理想,因為小批次的大小各不相同,有些甚至無法生成,因為文本小於 `nchars`。此外,過小的小批次無法充分利用 GPU 的性能。更明智的做法是從所有樣本中提取一大塊文本,然後生成所有的輸入-輸出對,將它們打亂,並生成大小相等的小批次。\n",
|
||||
"\n",
|
||||
"* **多層 LSTM**。嘗試使用 2 或 3 層的 LSTM 單元是有意義的。如我們在上一單元提到的,每一層 LSTM 都會從文本中提取某些模式,而在字符級生成器的情況下,我們可以預期較低層的 LSTM 負責提取音節,而較高層則負責單詞和單詞組合。這可以通過向 LSTM 構造函數傳遞層數參數來簡單實現。\n",
|
||||
"\n",
|
||||
"* 你也可以嘗試使用 **GRU 單元**,看看哪種表現更好,還可以嘗試 **不同的隱藏層大小**。過大的隱藏層可能導致過擬合(例如,網絡會學習到精確的文本),而過小的隱藏層可能無法產生良好的結果。\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## 軟文本生成與溫度\n",
|
||||
"\n",
|
||||
"在之前 `generate` 的定義中,我們總是選擇機率最高的字符作為生成文本中的下一個字符。這導致生成的文本經常在相同的字符序列之間「循環」出現,如以下例子所示:\n",
|
||||
"```\n",
|
||||
"today of the second the company and a second the company ...\n",
|
||||
"```\n",
|
||||
"\n",
|
||||
"然而,如果我們查看下一個字符的機率分佈,可能會發現幾個最高機率之間的差距並不大,例如一個字符的機率是 0.2,另一個是 0.19,等等。例如,在尋找序列 '*play*' 的下一個字符時,下一個字符可能同樣有可能是空格,或者是 **e**(如單詞 *player* 中的情況)。\n",
|
||||
"\n",
|
||||
"這讓我們得出一個結論:選擇機率最高的字符並不總是「公平」的,因為選擇第二高的字符仍然可能生成有意義的文本。更明智的做法是從網絡輸出的機率分佈中**抽樣**字符。\n",
|
||||
"\n",
|
||||
"這種抽樣可以使用 `multinomial` 函數來完成,該函數實現了所謂的**多項分佈**。下面定義了一個實現這種**軟**文本生成的函數:\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 10,
|
||||
"metadata": {
|
||||
"scrolled": true
|
||||
},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"--- Temperature = 0.3\n",
|
||||
"Today and a company and complete an all the land the restrational the as a security and has provers the pay to and a report and the computer in the stand has filities and working the law the stations for a company and with the company and the final the first company and refight of the state and and workin\n",
|
||||
"\n",
|
||||
"--- Temperature = 0.8\n",
|
||||
"Today he oniis its first to Aus bomblaties the marmation a to manan boogot that pirate assaid a relaid their that goverfin the the Cappets Ecrotional Assonia Cition targets it annight the w scyments Blamity #39;s TVeer Diercheg Reserals fran envyuil that of ster said access what succers of Dour-provelith\n",
|
||||
"\n",
|
||||
"--- Temperature = 1.0\n",
|
||||
"Today holy they a 11 will meda a toket subsuaties, engins for Chanos, they's has stainger past to opening orital his thempting new Nattona was al innerforder advan-than #36;s night year his religuled talitatian what the but with Wednesday to Justment will wemen of Mark CCC Camp as Timed Nae wome a leaders\n",
|
||||
"\n",
|
||||
"--- Temperature = 1.3\n",
|
||||
"Today gpone 2.5 fech atcusion poor cocles toparsdorM.cht Line Pamage put 43 his calt lowed to the book, that has authh-the silia rruch ailing to'ory andhes beutirsimi- Aefffive heading offil an auf eacklets is charged evis, Gunymy oy) Mony has it after-sloythyor loveId out filme, the Natabl -Najuntaxiggs \n",
|
||||
"\n",
|
||||
"--- Temperature = 1.8\n",
|
||||
"Today plary, P.slan chly\\401 mardregationly #39;t 8.1Mide) closes ,filtcon alfly playin roven!\\grea.-QFBEP: Iss onfarchQ/itilia CCf Zivesigntwasta orce.-Peul-aw.uicrin of fuglinfsut aftaningwo, MIEX awayew Aice Woiduar Corvagiugge oppo esig ThusBratourid canthly-RyI.co lagitems\\eexciaishes.conBabntusmor I\n",
|
||||
"\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"def generate_soft(net,size=100,start='today ',temperature=1.0):\n",
|
||||
" chars = list(start)\n",
|
||||
" out, s = net(enc(chars).view(1,-1).to(device))\n",
|
||||
" for i in range(size):\n",
|
||||
" #nc = torch.argmax(out[0][-1])\n",
|
||||
" out_dist = out[0][-1].div(temperature).exp()\n",
|
||||
" nc = torch.multinomial(out_dist,1)[0]\n",
|
||||
" chars.append(vocab.get_itos()[nc])\n",
|
||||
" out, s = net(nc.view(1,-1),s)\n",
|
||||
" return ''.join(chars)\n",
|
||||
" \n",
|
||||
"for i in [0.3,0.8,1.0,1.3,1.8]:\n",
|
||||
" print(f\"--- Temperature = {i}\\n{generate_soft(net,size=300,start='Today ',temperature=i)}\\n\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"我們引入了一個名為 **temperature** 的參數,用於指示我們應該多大程度地堅持最高概率。如果 temperature 是 1.0,我們進行公平的多項式抽樣,而當 temperature 趨於無窮大時,所有概率變得相等,我們隨機選擇下一個字符。在下面的例子中,我們可以觀察到,當我們將 temperature 增加得太高時,文本變得毫無意義,而當它接近 0 時,則類似於「循環」的硬生成文本。\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"\n---\n\n**免責聲明**: \n本文件已使用人工智能翻譯服務 [Co-op Translator](https://github.com/Azure/co-op-translator) 進行翻譯。我們致力於提供準確的翻譯,但請注意,自動翻譯可能包含錯誤或不準確之處。應以原文文件作為權威來源。對於關鍵資訊,建議尋求專業人工翻譯。我們對因使用此翻譯而引起的任何誤解或誤釋不承擔責任。\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"interpreter": {
|
||||
"hash": "16af2a8bbb083ea23e5e41c7f5787656b2ce26968575d8763f2c4b17f9cd711f"
|
||||
},
|
||||
"kernelspec": {
|
||||
"display_name": "Python 3.8.12 ('py38')",
|
||||
"language": "python",
|
||||
"name": "python3"
|
||||
},
|
||||
"language_info": {
|
||||
"codemirror_mode": {
|
||||
"name": "ipython",
|
||||
"version": 3
|
||||
},
|
||||
"file_extension": ".py",
|
||||
"mimetype": "text/x-python",
|
||||
"name": "python",
|
||||
"nbconvert_exporter": "python",
|
||||
"pygments_lexer": "ipython3",
|
||||
"version": "3.8.12"
|
||||
},
|
||||
"coopTranslator": {
|
||||
"original_hash": "7673cd150d96c74c6d6011460094efb4",
|
||||
"translation_date": "2025-08-31T10:35:18+00:00",
|
||||
"source_file": "lessons/5-NLP/17-GenerativeNetworks/GenerativePyTorch.ipynb",
|
||||
"language_code": "hk"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 4
|
||||
}
|
||||
|
|
@ -0,0 +1,497 @@
|
|||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"# 生成式網絡\n",
|
||||
"\n",
|
||||
"循環神經網絡(Recurrent Neural Networks, RNNs)及其門控單元變體,例如長短期記憶單元(Long Short Term Memory Cells, LSTMs)和門控循環單元(Gated Recurrent Units, GRUs),提供了一種語言建模的機制,也就是說,它們可以學習單詞的排列順序,並對序列中的下一個單詞進行預測。這使得我們可以使用 RNNs 來完成**生成任務**,例如普通文本生成、機器翻譯,甚至是圖像描述生成。\n",
|
||||
"\n",
|
||||
"在上一單元中討論的 RNN 結構中,每個 RNN 單元會輸出下一個隱藏狀態。然而,我們也可以為每個循環單元添加另一個輸出,這樣就可以輸出一個**序列**(其長度與原始序列相等)。此外,我們還可以使用不在每一步接受輸入的 RNN 單元,而僅僅接受一個初始狀態向量,然後生成一個輸出序列。\n",
|
||||
"\n",
|
||||
"在這個筆記本中,我們將專注於幫助我們生成文本的簡單生成模型。為了簡化,我們將構建一個**字符級網絡**,逐字母生成文本。在訓練過程中,我們需要使用一些文本語料庫,並將其拆分為字母序列。\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 1,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import tensorflow as tf\n",
|
||||
"from tensorflow import keras\n",
|
||||
"import tensorflow_datasets as tfds\n",
|
||||
"import numpy as np\n",
|
||||
"\n",
|
||||
"ds_train, ds_test = tfds.load('ag_news_subset').values()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## 建立字符詞彙表\n",
|
||||
"\n",
|
||||
"要建立字符級生成網絡,我們需要將文本拆分為單個字符,而不是單詞。我們之前使用的 `TextVectorization` 層無法做到這一點,因此我們有以下兩個選擇:\n",
|
||||
"\n",
|
||||
"* 手動載入文本並自行進行分詞,如 [這個官方 Keras 範例](https://keras.io/examples/generative/lstm_character_level_text_generation/) 中所示\n",
|
||||
"* 使用 `Tokenizer` 類進行字符級分詞。\n",
|
||||
"\n",
|
||||
"我們會選擇第二個選項。`Tokenizer` 也可以用於分詞成單詞,因此可以輕鬆地從字符級分詞切換到單詞級分詞。\n",
|
||||
"\n",
|
||||
"要進行字符級分詞,我們需要傳入 `char_level=True` 參數:\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 2,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"def extract_text(x):\n",
|
||||
" return x['title']+' '+x['description']\n",
|
||||
"\n",
|
||||
"def tupelize(x):\n",
|
||||
" return (extract_text(x),x['label'])\n",
|
||||
"\n",
|
||||
"tokenizer = keras.preprocessing.text.Tokenizer(char_level=True,lower=False)\n",
|
||||
"tokenizer.fit_on_texts([x['title'].numpy().decode('utf-8') for x in ds_train])"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"我們還希望使用一個特殊的標記來表示**序列結束**,我們將其稱為 `<eos>`。讓我們手動將其添加到詞彙表中:\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 3,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"eos_token = len(tokenizer.word_index)+1\n",
|
||||
"tokenizer.word_index['<eos>'] = eos_token\n",
|
||||
"\n",
|
||||
"vocab_size = eos_token + 1"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"現在,要將文本編碼為數字序列,我們可以使用:\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 4,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"[[48, 2, 10, 10, 5, 44, 1, 25, 5, 8, 10, 13, 78]]"
|
||||
]
|
||||
},
|
||||
"execution_count": 4,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"tokenizer.texts_to_sequences(['Hello, world!'])"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## 訓練生成式 RNN 來生成標題\n",
|
||||
"\n",
|
||||
"我們將以以下方式訓練 RNN 來生成新聞標題。在每一步中,我們會取一個標題,將其輸入到 RNN 中,並對於每個輸入的字符,要求網絡生成下一個輸出的字符:\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"對於序列中的最後一個字符,我們會要求網絡生成 `<eos>` 標記。\n",
|
||||
"\n",
|
||||
"我們在這裡使用的生成式 RNN 的主要不同之處在於,我們會從 RNN 的每一步輸出中取結果,而不僅僅是從最後一個單元格中取結果。這可以通過為 RNN 單元格指定 `return_sequences` 參數來實現。\n",
|
||||
"\n",
|
||||
"因此,在訓練過程中,網絡的輸入將是一個特定長度的編碼字符序列,而輸出將是一個相同長度的序列,但向後偏移一個元素並以 `<eos>` 結尾。小批量數據將由若干這樣的序列組成,我們需要使用**填充**來對齊所有序列。\n",
|
||||
"\n",
|
||||
"現在讓我們創建一些函數來幫助我們轉換數據集。由於我們希望在小批量層級上填充序列,我們會先通過調用 `.batch()` 將數據集分批,然後使用 `map` 來進行轉換。因此,轉換函數將以整個小批量作為參數:\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 5,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"def title_batch(x):\n",
|
||||
" x = [t.numpy().decode('utf-8') for t in x]\n",
|
||||
" z = tokenizer.texts_to_sequences(x)\n",
|
||||
" z = tf.keras.preprocessing.sequence.pad_sequences(z)\n",
|
||||
" return tf.one_hot(z,vocab_size), tf.one_hot(tf.concat([z[:,1:],tf.constant(eos_token,shape=(len(z),1))],axis=1),vocab_size)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"一些重要的事情我們在這裡做:\n",
|
||||
"* 我們首先從字串張量中提取實際文本\n",
|
||||
"* `text_to_sequences` 將字串列表轉換為整數張量列表\n",
|
||||
"* `pad_sequences` 將這些張量填充到它們的最大長度\n",
|
||||
"* 最後,我們對所有字符進行 one-hot 編碼,並進行移位和附加 `<eos>`。我們很快就會看到為什麼需要 one-hot 編碼的字符\n",
|
||||
"\n",
|
||||
"然而,這個函數是 **Pythonic** 的,也就是說它無法自動轉換為 Tensorflow 的計算圖。如果我們直接在 `Dataset.map` 函數中使用這個函數,會出現錯誤。我們需要使用 `py_function` 包裝器來封裝這個 Pythonic 調用:\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 6,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"def title_batch_fn(x):\n",
|
||||
" x = x['title']\n",
|
||||
" a,b = tf.py_function(title_batch,inp=[x],Tout=(tf.float32,tf.float32))\n",
|
||||
" return a,b"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"> **注意**:區分 Pythonic 和 Tensorflow 的轉換函數可能看起來有點複雜,你可能會疑惑為什麼我們不在將數據集傳遞給 `fit` 之前,使用標準的 Python 函數進行轉換。雖然這確實是可行的,但使用 `Dataset.map` 有一個巨大的優勢,因為數據轉換管道是通過 Tensorflow 的計算圖執行的,這可以利用 GPU 的計算能力,並且減少了在 CPU 和 GPU 之間傳遞數據的需求。\n",
|
||||
"\n",
|
||||
"現在我們可以建立生成器網絡並開始訓練。它可以基於我們在上一單元討論過的任何循環單元(簡單的、LSTM 或 GRU)。在我們的例子中,我們將使用 LSTM。\n",
|
||||
"\n",
|
||||
"由於網絡以字符作為輸入,且詞彙表的大小相對較小,我們不需要嵌入層,直接使用 one-hot 編碼的輸入進入 LSTM 單元即可。輸出層將是一個 `Dense` 分類器,用於將 LSTM 的輸出轉換為 one-hot 編碼的標籤數字。\n",
|
||||
"\n",
|
||||
"此外,因為我們處理的是可變長度的序列,我們可以使用 `Masking` 層來創建一個掩碼,忽略字符串中填充的部分。這並不是絕對必要的,因為我們對 `<eos>` 標籤之後的內容並不特別感興趣,但我們會使用它來獲得一些使用這類型層的經驗。`input_shape` 將是 `(None, vocab_size)`,其中 `None` 表示可變長度的序列,而輸出形狀也是 `(None, vocab_size)`,正如你可以從 `summary` 中看到的那樣。\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 7,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"Model: \"sequential\"\n",
|
||||
"_________________________________________________________________\n",
|
||||
"Layer (type) Output Shape Param # \n",
|
||||
"=================================================================\n",
|
||||
"masking (Masking) (None, None, 84) 0 \n",
|
||||
"_________________________________________________________________\n",
|
||||
"lstm (LSTM) (None, None, 128) 109056 \n",
|
||||
"_________________________________________________________________\n",
|
||||
"dense (Dense) (None, None, 84) 10836 \n",
|
||||
"=================================================================\n",
|
||||
"Total params: 119,892\n",
|
||||
"Trainable params: 119,892\n",
|
||||
"Non-trainable params: 0\n",
|
||||
"_________________________________________________________________\n",
|
||||
"15000/15000 [==============================] - 229s 15ms/step - loss: 1.5385\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"<tensorflow.python.keras.callbacks.History at 0x7fa40c1245e0>"
|
||||
]
|
||||
},
|
||||
"execution_count": 7,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"model = keras.models.Sequential([\n",
|
||||
" keras.layers.Masking(input_shape=(None,vocab_size)),\n",
|
||||
" keras.layers.LSTM(128,return_sequences=True),\n",
|
||||
" keras.layers.Dense(vocab_size,activation='softmax')\n",
|
||||
"])\n",
|
||||
"\n",
|
||||
"model.summary()\n",
|
||||
"model.compile(loss='categorical_crossentropy')\n",
|
||||
"\n",
|
||||
"model.fit(ds_train.batch(8).map(title_batch_fn))"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## 生成輸出\n",
|
||||
"\n",
|
||||
"現在我們已經訓練了模型,接下來我們希望使用它來生成一些輸出。首先,我們需要一種方法來解碼由一系列標記數字表示的文本。為此,我們可以使用 `tokenizer.sequences_to_texts` 函數;然而,這個方法在字符級標記化時效果不佳。因此,我們將從 tokenizer 中提取標記字典(稱為 `word_index`),構建一個反向映射,並編寫我們自己的解碼函數:\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 10,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"reverse_map = {val:key for key, val in tokenizer.word_index.items()}\n",
|
||||
"\n",
|
||||
"def decode(x):\n",
|
||||
" return ''.join([reverse_map[t] for t in x])"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"現在,我們開始進行生成。我們會從某個字串 `start` 開始,將其編碼成一個序列 `inp`,然後在每一步調用我們的網絡來推斷下一個字符。\n",
|
||||
"\n",
|
||||
"網絡的輸出 `out` 是一個包含 `vocab_size` 個元素的向量,代表每個標記的概率。我們可以通過使用 `argmax` 找到最有可能的標記編號。接著,我們將這個字符附加到已生成的標記列表中,並繼續生成過程。這個生成一個字符的過程會重複執行 `size` 次,以生成所需數量的字符。如果在過程中遇到 `eos_token`,我們會提前終止生成。\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 12,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"'Today #39;s lead to strike for the strike for the strike for the strike (AFP)'"
|
||||
]
|
||||
},
|
||||
"execution_count": 12,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"def generate(model,size=100,start='Today '):\n",
|
||||
" inp = tokenizer.texts_to_sequences([start])[0]\n",
|
||||
" chars = inp\n",
|
||||
" for i in range(size):\n",
|
||||
" out = model(tf.expand_dims(tf.one_hot(inp,vocab_size),0))[0][-1]\n",
|
||||
" nc = tf.argmax(out)\n",
|
||||
" if nc==eos_token:\n",
|
||||
" break\n",
|
||||
" chars.append(nc.numpy())\n",
|
||||
" inp = inp+[nc]\n",
|
||||
" return decode(chars)\n",
|
||||
" \n",
|
||||
"generate(model)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## 在訓練期間抽樣輸出\n",
|
||||
"\n",
|
||||
"由於我們沒有任何有用的指標,例如 *準確率*,唯一能判斷模型是否有所改善的方法就是在訓練期間**抽樣**生成的字串。為了實現這一點,我們會使用**回調函數**,即可以傳遞給 `fit` 函數的函數,並在訓練過程中定期被調用。\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 13,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"Epoch 1/3\n",
|
||||
"15000/15000 [==============================] - 226s 15ms/step - loss: 1.2703\n",
|
||||
"Today #39;s a lead in the company for the strike\n",
|
||||
"Epoch 2/3\n",
|
||||
"15000/15000 [==============================] - 227s 15ms/step - loss: 1.2057\n",
|
||||
"Today #39;s the Market Service on Security Start (AP)\n",
|
||||
"Epoch 3/3\n",
|
||||
"15000/15000 [==============================] - 226s 15ms/step - loss: 1.1752\n",
|
||||
"Today #39;s a line on the strike to start for the start\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"<tensorflow.python.keras.callbacks.History at 0x7fa40c74e3d0>"
|
||||
]
|
||||
},
|
||||
"execution_count": 13,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"sampling_callback = keras.callbacks.LambdaCallback(\n",
|
||||
" on_epoch_end = lambda batch, logs: print(generate(model))\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"model.fit(ds_train.batch(8).map(title_batch_fn),callbacks=[sampling_callback],epochs=3)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"這個例子已經生成了一些相當不錯的文本,但仍有幾個方面可以進一步改進:\n",
|
||||
"\n",
|
||||
"* **更多文本**。我們只使用了標題來進行任務,但你可能想嘗試使用完整的文本。請記住,RNN在處理長序列時表現並不太好,因此可以考慮將它們拆分成較短的句子,或者始終在固定的序列長度(例如,`num_chars`,假設為256)上進行訓練。你可以嘗試將上面的例子改造成這樣的架構,並參考[官方 Keras 教程](https://keras.io/examples/generative/lstm_character_level_text_generation/)作為靈感。\n",
|
||||
"\n",
|
||||
"* **多層 LSTM**。可以嘗試使用2層或3層的LSTM單元。如我們在前一單元提到的,每一層LSTM會從文本中提取特定的模式,而在字符級生成器的情況下,我們可以預期較低層的LSTM負責提取音節,而較高層則負責提取單詞和單詞組合。這可以通過向LSTM構造函數傳遞層數參數來簡單實現。\n",
|
||||
"\n",
|
||||
"* 你可能還想嘗試使用**GRU單元**,看看哪種表現更好,或者嘗試**不同的隱藏層大小**。隱藏層過大可能導致過擬合(例如,網絡會學習到精確的文本),而過小的大小可能無法產生良好的結果。\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## 軟性文本生成與溫度\n",
|
||||
"\n",
|
||||
"在之前 `generate` 的定義中,我們總是選擇機率最高的字符作為生成文本中的下一個字符。這導致文本經常在相同的字符序列之間「循環」出現,如以下例子所示:\n",
|
||||
"```\n",
|
||||
"today of the second the company and a second the company ...\n",
|
||||
"```\n",
|
||||
"\n",
|
||||
"然而,如果我們查看下一個字符的機率分佈,可能會發現幾個最高機率之間的差距並不大,例如一個字符的機率是 0.2,另一個是 0.19,等等。例如,在尋找序列 *play* 的下一個字符時,下一個字符可能同樣有可能是空格,或者是 **e**(如單詞 *player* 中的情況)。\n",
|
||||
"\n",
|
||||
"這讓我們得出一個結論:選擇機率最高的字符並不總是「公平」的,因為選擇第二高的字符仍然可能生成有意義的文本。更明智的做法是從網絡輸出的機率分佈中**抽樣**字符。\n",
|
||||
"\n",
|
||||
"這種抽樣可以使用 `np.multinomial` 函數來完成,該函數實現了所謂的**多項分佈**。下面定義了一個實現這種**軟性**文本生成的函數:\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 33,
|
||||
"metadata": {
|
||||
"scrolled": true
|
||||
},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"\n",
|
||||
"--- Temperature = 0.3\n",
|
||||
"Today #39;s strike #39; to start at the store return\n",
|
||||
"On Sunday PO to Be Data Profit Up (Reuters)\n",
|
||||
"Moscow, SP wins straight to the Microsoft #39;s control of the space start\n",
|
||||
"President olding of the blast start for the strike to pay <b>...</b>\n",
|
||||
"Little red riding hood ficed to the spam countered in European <b>...</b>\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 <b>...</b>\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": [
|
||||
"我們引入了一個名為 **temperature** 的參數,用於指示我們應該多大程度地堅持最高概率。如果 temperature 是 1.0,我們進行公平的多項式抽樣,而當 temperature 趨於無窮大時,所有概率變得相等,我們隨機選擇下一個字符。在下面的例子中,我們可以觀察到當我們將 temperature 增加得太多時,文本變得毫無意義,而當它接近 0 時,則類似於「循環」的硬生成文本。\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"\n---\n\n**免責聲明**: \n本文件已使用人工智能翻譯服務 [Co-op Translator](https://github.com/Azure/co-op-translator) 進行翻譯。儘管我們致力於提供準確的翻譯,但請注意,自動翻譯可能包含錯誤或不準確之處。原始語言的文件應被視為權威來源。對於重要資訊,建議使用專業人工翻譯。我們對因使用此翻譯而引起的任何誤解或錯誤解釋概不負責。\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"interpreter": {
|
||||
"hash": "16af2a8bbb083ea23e5e41c7f5787656b2ce26968575d8763f2c4b17f9cd711f"
|
||||
},
|
||||
"kernelspec": {
|
||||
"display_name": "Python 3.8.12 ('py38')",
|
||||
"language": "python",
|
||||
"name": "python3"
|
||||
},
|
||||
"language_info": {
|
||||
"codemirror_mode": {
|
||||
"name": "ipython",
|
||||
"version": 3
|
||||
},
|
||||
"file_extension": ".py",
|
||||
"mimetype": "text/x-python",
|
||||
"name": "python",
|
||||
"nbconvert_exporter": "python",
|
||||
"pygments_lexer": "ipython3",
|
||||
"version": "3.8.12"
|
||||
},
|
||||
"coopTranslator": {
|
||||
"original_hash": "9fbb7d5fda708537649f71f5f646fcde",
|
||||
"translation_date": "2025-08-31T10:33:22+00:00",
|
||||
"source_file": "lessons/5-NLP/17-GenerativeNetworks/GenerativeTF.ipynb",
|
||||
"language_code": "hk"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 4
|
||||
}
|
||||
|
|
@ -0,0 +1,79 @@
|
|||
# 生成式網絡
|
||||
|
||||
## [課前測驗](https://ff-quizzes.netlify.app/en/ai/quiz/33)
|
||||
|
||||
循環神經網絡(RNN)及其門控單元變體,例如長短期記憶單元(LSTM)和門控循環單元(GRU),提供了一種語言建模的機制,能夠學習詞語的排序並預測序列中的下一個詞語。這使得我們可以使用 RNN 進行**生成任務**,例如普通文本生成、機器翻譯,甚至是圖片描述。
|
||||
|
||||
> ✅ 想想你在使用生成任務(例如輸入時的文本補全)時受益的情況。研究一下你喜愛的應用程序,看看它們是否使用了 RNN。
|
||||
|
||||
在上一單元中討論的 RNN 架構中,每個 RNN 單元都生成下一個隱藏狀態作為輸出。然而,我們也可以為每個循環單元添加另一個輸出,這樣就可以輸出一個**序列**(與原始序列的長度相等)。此外,我們可以使用不在每一步接受輸入的 RNN 單元,只需一些初始狀態向量,然後生成一系列輸出。
|
||||
|
||||
這使得不同的神經架構成為可能,如下圖所示:
|
||||
|
||||

|
||||
|
||||
> 圖片來自 [Andrej Karpaty](http://karpathy.github.io/) 的博客文章 [Unreasonable Effectiveness of Recurrent Neural Networks](http://karpathy.github.io/2015/05/21/rnn-effectiveness/)
|
||||
|
||||
* **一對一** 是傳統的神經網絡,具有一個輸入和一個輸出
|
||||
* **一對多** 是一種生成式架構,接受一個輸入值,並生成一系列輸出值。例如,如果我們想訓練一個**圖片描述**網絡來生成圖片的文字描述,我們可以將圖片作為輸入,通過 CNN 獲得其隱藏狀態,然後使用循環鏈逐字生成描述
|
||||
* **多對一** 對應於我們在上一單元中描述的 RNN 架構,例如文本分類
|
||||
* **多對多** 或 **序列對序列** 對應於任務,例如**機器翻譯**,其中第一個 RNN 收集輸入序列中的所有信息到隱藏狀態,然後另一個 RNN 鏈將該狀態展開為輸出序列。
|
||||
|
||||
在本單元中,我們將重點放在幫助生成文本的簡單生成模型上。為了簡化,我們將使用字符級標記化。
|
||||
|
||||
我們將訓練這個 RNN 逐步生成文本。在每一步中,我們將取一個長度為 `nchars` 的字符序列,並要求網絡為每個輸入字符生成下一個輸出字符:
|
||||
|
||||

|
||||
|
||||
在生成文本(推理過程中)時,我們從某個**提示**開始,將其通過 RNN 單元生成中間狀態,然後從該狀態開始生成。我們一次生成一個字符,並將狀態和生成的字符傳遞給另一個 RNN 單元以生成下一個字符,直到生成足夠的字符。
|
||||
|
||||
<img src="../../../../../translated_images/zh-HK/rnn-generate-inf.5168dc65e0370eea.webp" width="60%"/>
|
||||
|
||||
> 圖片由作者提供
|
||||
|
||||
## ✍️ 練習:生成式網絡
|
||||
|
||||
在以下筆記本中繼續學習:
|
||||
|
||||
* [使用 PyTorch 的生成式網絡](GenerativePyTorch.ipynb)
|
||||
* [使用 TensorFlow 的生成式網絡](GenerativeTF.ipynb)
|
||||
|
||||
## 軟文本生成與溫度
|
||||
|
||||
每個 RNN 單元的輸出是一個字符的概率分佈。如果我們總是選擇概率最高的字符作為生成文本的下一個字符,文本可能會反覆循環相同的字符序列,如以下示例所示:
|
||||
|
||||
```
|
||||
today of the second the company and a second the company ...
|
||||
```
|
||||
|
||||
然而,如果我們查看下一個字符的概率分佈,可能幾個最高概率之間的差距並不大,例如一個字符的概率可能是 0.2,另一個是 0.19,等等。例如,在尋找序列 '*play*' 的下一個字符時,下一個字符可能同樣是空格或 **e**(如單詞 *player*)。
|
||||
|
||||
這使我們得出結論,選擇概率最高的字符並不總是“公平”的,因為選擇第二高的字符仍然可能生成有意義的文本。更明智的做法是從網絡輸出的概率分佈中**抽樣**字符。我們還可以使用一個參數 **溫度**,來平滑概率分佈,以增加隨機性,或者使分佈更陡峭,以更傾向於選擇最高概率的字符。
|
||||
|
||||
在上述筆記本中探索如何實現這種軟文本生成。
|
||||
|
||||
## 結論
|
||||
|
||||
雖然文本生成本身可能很有用,但主要的好處來自於能夠使用 RNN 從某些初始特徵向量生成文本。例如,文本生成可用於機器翻譯(序列對序列,在此情況下,*編碼器* 的狀態向量用於生成或*解碼*翻譯的消息),或生成圖片的文字描述(在此情況下,特徵向量來自 CNN 提取器)。
|
||||
|
||||
## 🚀 挑戰
|
||||
|
||||
在 Microsoft Learn 上學習相關主題的課程
|
||||
|
||||
* 使用 [PyTorch](https://docs.microsoft.com/learn/modules/intro-natural-language-processing-pytorch/6-generative-networks/?WT.mc_id=academic-77998-cacaste)/[TensorFlow](https://docs.microsoft.com/learn/modules/intro-natural-language-processing-tensorflow/5-generative-networks/?WT.mc_id=academic-77998-cacaste) 進行文本生成
|
||||
|
||||
## [課後測驗](https://ff-quizzes.netlify.app/en/ai/quiz/34)
|
||||
|
||||
## 回顧與自學
|
||||
|
||||
以下文章可擴展您的知識:
|
||||
|
||||
* 使用馬爾可夫鏈、LSTM 和 GPT-2 進行文本生成的不同方法:[博客文章](https://towardsdatascience.com/text-generation-gpt-2-lstm-markov-chain-9ea371820e1e)
|
||||
* Keras 文檔中的文本生成示例:[Keras 文檔](https://keras.io/examples/generative/lstm_character_level_text_generation/)
|
||||
|
||||
## [作業](lab/README.md)
|
||||
|
||||
我們已經了解了如何逐字符生成文本。在實驗中,您將探索逐詞文本生成。
|
||||
|
||||
---
|
||||
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
# 使用 RNN 進行詞級文本生成
|
||||
|
||||
來自 [AI for Beginners Curriculum](https://github.com/microsoft/ai-for-beginners) 的實驗課題。
|
||||
|
||||
## 任務
|
||||
|
||||
在這次實驗中,你需要選擇一本書,並使用它作為數據集來訓練詞級文本生成器。
|
||||
|
||||
## 數據集
|
||||
|
||||
你可以使用任何一本書作為數據集。你可以在 [Project Gutenberg](https://www.gutenberg.org/) 找到許多免費的文本,例如,這裡有 Lewis Carroll 的《愛麗絲夢遊仙境》的直接連結:[Alice's Adventures in Wonderland](https://www.gutenberg.org/files/11/11-0.txt)。
|
||||
|
||||
**免責聲明**:
|
||||
本文件已使用人工智能翻譯服務 [Co-op Translator](https://github.com/Azure/co-op-translator) 進行翻譯。我們致力於提供準確的翻譯,但請注意,自動翻譯可能包含錯誤或不準確之處。應以原文文件作為權威來源。對於關鍵資訊,建議尋求專業人工翻譯。我們對因使用此翻譯而引起的任何誤解或誤釋概不負責。
|
||||
|
|
@ -0,0 +1,112 @@
|
|||
# 注意力機制與Transformer
|
||||
|
||||
## [課前測驗](https://ff-quizzes.netlify.app/en/ai/quiz/35)
|
||||
|
||||
自然語言處理(NLP)領域中最重要的問題之一是**機器翻譯**,這是一項支撐工具(如Google翻譯)的核心任務。在本節中,我們將專注於機器翻譯,或者更廣泛地說,任何*序列到序列*的任務(也稱為**句子轉換**)。
|
||||
|
||||
使用RNN時,序列到序列的實現是通過兩個遞歸網絡完成的,其中一個網絡(**編碼器**)將輸入序列壓縮成隱藏狀態,而另一個網絡(**解碼器**)將該隱藏狀態展開為翻譯結果。然而,這種方法存在一些問題:
|
||||
|
||||
* 編碼器網絡的最終狀態很難記住句子的開頭,導致模型對長句子的質量較差。
|
||||
* 序列中的所有詞對結果的影響相同。然而,實際上,輸入序列中的某些特定詞對輸出序列的影響往往更大。
|
||||
|
||||
**注意力機制**提供了一種方法,能夠對每個輸入向量對RNN輸出預測的上下文影響進行加權。其實現方式是通過在輸入RNN的中間狀態與輸出RNN之間創建捷徑。這樣,在生成輸出符號y<sub>t</sub>時,我們會考慮所有輸入隱藏狀態h<sub>i</sub>,並賦予不同的權重係數α<sub>t,i</sub>。
|
||||
|
||||

|
||||
|
||||
> [Bahdanau等人, 2015](https://arxiv.org/pdf/1409.0473.pdf)中的加性注意力機制編碼器-解碼器模型,圖片來源於[這篇博客文章](https://lilianweng.github.io/lil-log/2018/06/24/attention-attention.html)
|
||||
|
||||
注意力矩陣{α<sub>i,j</sub>}表示某些輸入詞在生成給定輸出序列中的某個詞時所起的作用程度。以下是一個這樣的矩陣示例:
|
||||
|
||||

|
||||
|
||||
> 圖片來自[Bahdanau等人, 2015](https://arxiv.org/pdf/1409.0473.pdf)(圖3)
|
||||
|
||||
注意力機制是當前或接近當前NLP領域技術前沿的關鍵原因之一。然而,添加注意力機制會大幅增加模型參數的數量,這導致了RNN的擴展問題。RNN的一個主要限制是其遞歸性質使得訓練過程難以批量化和並行化。在RNN中,序列的每個元素需要按順序處理,這意味著它無法輕易並行化。
|
||||
|
||||

|
||||
|
||||
> 圖片來自[Google的博客](https://research.googleblog.com/2016/09/a-neural-network-for-machine.html)
|
||||
|
||||
注意力機制的採用以及上述限制促成了如今我們所熟知和使用的技術前沿Transformer模型的誕生,例如BERT和Open-GPT3。
|
||||
|
||||
## Transformer模型
|
||||
|
||||
Transformer的主要思想之一是避免RNN的序列性質,並創建一個在訓練過程中可並行化的模型。這是通過實現以下兩個想法來實現的:
|
||||
|
||||
* 位置編碼
|
||||
* 使用自注意力機制來捕捉模式,而不是使用RNN(或CNN)(這也是為什麼介紹Transformer的論文被稱為*[Attention is all you need](https://arxiv.org/abs/1706.03762)*)
|
||||
|
||||
### 位置編碼/嵌入
|
||||
|
||||
位置編碼的想法如下:
|
||||
1. 使用RNN時,詞元的相對位置由步數表示,因此不需要顯式表示。
|
||||
2. 然而,一旦我們切換到注意力機制,我們需要知道序列中詞元的相對位置。
|
||||
3. 為了獲得位置編碼,我們將詞元序列與序列中的詞元位置序列(即數字序列0,1, ...)結合。
|
||||
4. 然後,我們將詞元位置與詞元嵌入向量混合。為了將位置(整數)轉換為向量,我們可以使用不同的方法:
|
||||
|
||||
* 可訓練的嵌入,類似於詞元嵌入。這是我們在此考慮的方法。我們在詞元和它們的位置上應用嵌入層,生成相同維度的嵌入向量,然後將它們相加。
|
||||
* 固定位置編碼函數,正如原始論文中提出的那樣。
|
||||
|
||||
<img src="../../../../../translated_images/zh-HK/pos-embedding.e41ce9b6cf6078af.webp" width="50%"/>
|
||||
|
||||
> 圖片由作者提供
|
||||
|
||||
通過位置嵌入,我們獲得的結果同時嵌入了原始詞元及其在序列中的位置。
|
||||
|
||||
### 多頭自注意力
|
||||
|
||||
接下來,我們需要捕捉序列中的一些模式。為此,Transformer使用了**自注意力**機制,這本質上是將注意力應用於相同的輸入和輸出序列。應用自注意力使我們能夠考慮句子中的**上下文**,並查看哪些詞是相互關聯的。例如,它使我們能夠看到哪些詞由指代詞(如*it*)指代,並考慮上下文:
|
||||
|
||||

|
||||
|
||||
> 圖片來自[Google博客](https://research.googleblog.com/2017/08/transformer-novel-neural-network.html)
|
||||
|
||||
在Transformer中,我們使用**多頭注意力**來賦予網絡捕捉多種類型依賴關係的能力,例如長期與短期詞關係、指代關係與其他關係等。
|
||||
|
||||
[TensorFlow Notebook](TransformersTF.ipynb)中包含有關Transformer層實現的更多細節。
|
||||
|
||||
### 編碼器-解碼器注意力
|
||||
|
||||
在Transformer中,注意力機制用於兩個地方:
|
||||
|
||||
* 使用自注意力捕捉輸入文本中的模式
|
||||
* 執行序列翻譯——這是編碼器與解碼器之間的注意力層。
|
||||
|
||||
編碼器-解碼器注意力與本節開頭描述的RNN中的注意力機制非常相似。以下動畫圖解釋了編碼器-解碼器注意力的作用。
|
||||
|
||||

|
||||
|
||||
由於每個輸入位置獨立映射到每個輸出位置,Transformer比RNN更容易並行化,這使得更大且更具表達力的語言模型成為可能。每個注意力頭可以用於學習詞之間的不同關係,從而改進下游自然語言處理任務。
|
||||
|
||||
## BERT
|
||||
|
||||
**BERT**(Bidirectional Encoder Representations from Transformers)是一個非常大的多層Transformer網絡,*BERT-base*有12層,*BERT-large*有24層。該模型首先在大規模文本數據語料庫(維基百科+書籍)上進行無監督預訓練(預測句子中的被遮蔽詞)。在預訓練過程中,模型吸收了大量的語言理解能力,這些能力可以通過微調其他數據集來利用。這個過程被稱為**遷移學習**。
|
||||
|
||||

|
||||
|
||||
> 圖片[來源](http://jalammar.github.io/illustrated-bert/)
|
||||
|
||||
## ✍️ 練習:Transformer
|
||||
|
||||
通過以下筆記本繼續學習:
|
||||
|
||||
* [PyTorch中的Transformer](TransformersPyTorch.ipynb)
|
||||
* [TensorFlow中的Transformer](TransformersTF.ipynb)
|
||||
|
||||
## 總結
|
||||
|
||||
在本課中,你學習了Transformer和注意力機制,這些都是NLP工具箱中的重要工具。Transformer架構有許多變體,包括BERT、DistilBERT、BigBird、OpenGPT3等,這些模型可以進行微調。[HuggingFace包](https://github.com/huggingface/)提供了使用PyTorch和TensorFlow訓練這些架構的資源庫。
|
||||
|
||||
## 🚀 挑戰
|
||||
|
||||
## [課後測驗](https://ff-quizzes.netlify.app/en/ai/quiz/36)
|
||||
|
||||
## 回顧與自學
|
||||
|
||||
* [博客文章](https://mchromiak.github.io/articles/2017/Sep/12/Transformer-Attention-is-all-you-need/),解釋了經典的[Attention is all you need](https://arxiv.org/abs/1706.03762) Transformer論文。
|
||||
* [一系列博客文章](https://towardsdatascience.com/transformers-explained-visually-part-1-overview-of-functionality-95a6dd460452),詳細解釋了Transformer的架構。
|
||||
|
||||
## [作業](assignment.md)
|
||||
|
||||
---
|
||||
|
||||
|
|
@ -0,0 +1,353 @@
|
|||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"# 注意力機制與Transformer\n",
|
||||
"\n",
|
||||
"循環神經網絡的一個主要缺點是序列中的所有詞語對結果的影響相同。這導致標準的LSTM編碼器-解碼器模型在序列到序列任務(例如命名實體識別和機器翻譯)中的表現不佳。實際上,輸入序列中的某些特定詞語通常比其他詞語對序列輸出有更大的影響。\n",
|
||||
"\n",
|
||||
"考慮一個序列到序列模型,例如機器翻譯。它由兩個循環神經網絡實現,其中一個網絡(**編碼器**)將輸入序列壓縮成隱藏狀態,另一個網絡(**解碼器**)將隱藏狀態展開成翻譯結果。這種方法的問題在於,網絡的最終狀態很難記住句子的開頭部分,從而導致模型在處理長句子時質量下降。\n",
|
||||
"\n",
|
||||
"**注意力機制**提供了一種方法,能夠對每個輸入向量對RNN每個輸出預測的上下文影響進行加權。其實現方式是通過在輸入RNN的中間狀態和輸出RNN之間創建捷徑。以此方式,在生成輸出符號$y_t$時,我們會考慮所有輸入隱藏狀態$h_i$,並賦予不同的權重係數$\\alpha_{t,i}$。\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"*具有加性注意力機制的編碼器-解碼器模型,來自 [Bahdanau et al., 2015](https://arxiv.org/pdf/1409.0473.pdf),引用自 [這篇博客文章](https://lilianweng.github.io/lil-log/2018/06/24/attention-attention.html)*\n",
|
||||
"\n",
|
||||
"注意力矩陣$\\{\\alpha_{i,j}\\}$表示某些輸入詞語在生成輸出序列中的某個詞語時所起的作用程度。以下是這樣一個矩陣的示例:\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"*圖片取自 [Bahdanau et al., 2015](https://arxiv.org/pdf/1409.0473.pdf) (圖3)*\n",
|
||||
"\n",
|
||||
"注意力機制是目前或接近目前自然語言處理領域的最先進技術的核心。添加注意力機制會大幅增加模型參數的數量,這導致了RNN的擴展問題。RNN擴展的一個關鍵限制是模型的循環性使得批量處理和並行化訓練變得困難。在RNN中,序列的每個元素都需要按順序處理,這意味著它無法輕易並行化。\n",
|
||||
"\n",
|
||||
"注意力機制的採用結合這一限制,促成了如今我們所熟知和使用的最先進Transformer模型的誕生,從BERT到OpenGPT3。\n",
|
||||
"\n",
|
||||
"## Transformer模型\n",
|
||||
"\n",
|
||||
"與將每次預測的上下文傳遞到下一個評估步驟不同,**Transformer模型**使用**位置編碼**和注意力來捕捉給定輸入在提供的文本窗口內的上下文。下圖展示了位置編碼與注意力如何在給定窗口內捕捉上下文。\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"由於每個輸入位置獨立映射到每個輸出位置,Transformer比RNN更容易並行化,這使得能夠構建更大、更具表達力的語言模型。每個注意力頭可以用來學習詞語之間的不同關係,從而改善下游的自然語言處理任務。\n",
|
||||
"\n",
|
||||
"**BERT**(Bidirectional Encoder Representations from Transformers,雙向編碼器表示)是一個非常大的多層Transformer網絡,*BERT-base*有12層,*BERT-large*有24層。該模型首先在大規模文本數據(維基百科+書籍)上進行無監督訓練(預測句子中的被遮蔽詞語)。在預訓練過程中,模型吸收了大量的語言理解能力,隨後可以通過微調其他數據集來利用這些能力。這個過程稱為**遷移學習**。\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"Transformer架構有許多變體,包括BERT、DistilBERT、BigBird、OpenGPT3等,這些模型都可以進行微調。[HuggingFace套件](https://github.com/huggingface/)提供了使用PyTorch訓練這些架構的資源庫。\n",
|
||||
"\n",
|
||||
"## 使用BERT進行文本分類\n",
|
||||
"\n",
|
||||
"讓我們看看如何使用預訓練的BERT模型來解決我們的傳統任務:序列分類。我們將對原始的AG News數據集進行分類。\n",
|
||||
"\n",
|
||||
"首先,載入HuggingFace庫和我們的數據集:\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 10,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"Loading dataset...\n",
|
||||
"Building vocab...\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"import torch\n",
|
||||
"import torchtext\n",
|
||||
"from torchnlp import *\n",
|
||||
"import transformers\n",
|
||||
"train_dataset, test_dataset, classes, vocab = load_dataset()\n",
|
||||
"vocab_len = len(vocab)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"因為我們將使用預訓練的 BERT 模型,所以需要使用特定的 tokenizer。首先,我們會載入與預訓練 BERT 模型相關聯的 tokenizer。\n",
|
||||
"\n",
|
||||
"HuggingFace 庫包含一個預訓練模型的資源庫,你只需在 `from_pretrained` 函數中指定模型名稱作為參數即可使用。所有模型所需的二進制文件會自動下載。\n",
|
||||
"\n",
|
||||
"然而,有時你可能需要載入自己的模型,在這種情況下,你可以指定包含所有相關文件的目錄,包括 tokenizer 的參數、包含模型參數的 `config.json` 文件、二進制權重等。\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 11,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# To load the model from Internet repository using model name. \n",
|
||||
"# Use this if you are running from your own copy of the notebooks\n",
|
||||
"bert_model = 'bert-base-uncased' \n",
|
||||
"\n",
|
||||
"# To load the model from the directory on disk. Use this for Microsoft Learn module, because we have\n",
|
||||
"# prepared all required files for you.\n",
|
||||
"bert_model = './bert'\n",
|
||||
"\n",
|
||||
"tokenizer = transformers.BertTokenizer.from_pretrained(bert_model)\n",
|
||||
"\n",
|
||||
"MAX_SEQ_LEN = 128\n",
|
||||
"PAD_INDEX = tokenizer.convert_tokens_to_ids(tokenizer.pad_token)\n",
|
||||
"UNK_INDEX = tokenizer.convert_tokens_to_ids(tokenizer.unk_token)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"`tokenizer` 對象包含 `encode` 函數,可直接用於編碼文本:\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 15,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"[101, 1052, 22123, 2953, 2818, 2003, 1037, 2307, 7705, 2005, 17953, 2361, 102]"
|
||||
]
|
||||
},
|
||||
"execution_count": 15,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"tokenizer.encode('PyTorch is a great framework for NLP')"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"然後,我們來建立在訓練期間用於訪問數據的迭代器。由於 BERT 使用其自己的編碼函數,我們需要定義一個類似於之前定義的 `padify` 的填充函數:\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 4,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"def pad_bert(b):\n",
|
||||
" # b is the list of tuples of length batch_size\n",
|
||||
" # - first element of a tuple = label, \n",
|
||||
" # - second = feature (text sequence)\n",
|
||||
" # build vectorized sequence\n",
|
||||
" v = [tokenizer.encode(x[1]) for x in b]\n",
|
||||
" # compute max length of a sequence in this minibatch\n",
|
||||
" l = max(map(len,v))\n",
|
||||
" return ( # tuple of two tensors - labels and features\n",
|
||||
" torch.LongTensor([t[0] for t in b]),\n",
|
||||
" torch.stack([torch.nn.functional.pad(torch.tensor(t),(0,l-len(t)),mode='constant',value=0) for t in v])\n",
|
||||
" )\n",
|
||||
"\n",
|
||||
"train_loader = torch.utils.data.DataLoader(train_dataset, batch_size=8, collate_fn=pad_bert, shuffle=True)\n",
|
||||
"test_loader = torch.utils.data.DataLoader(test_dataset, batch_size=8, collate_fn=pad_bert)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"在我們的情況下,我們將使用名為 `bert-base-uncased` 的預訓練 BERT 模型。讓我們使用 `BertForSequenceClassfication` 套件來加載模型。這確保了我們的模型已經具備分類所需的架構,包括最終的分類器。您會看到一條警告訊息,指出最終分類器的權重尚未初始化,模型需要進行預訓練——這完全沒問題,因為這正是我們即將要做的!\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 9,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stderr",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"Some weights of the model checkpoint at ./bert were not used when initializing BertForSequenceClassification: ['cls.predictions.bias', 'cls.predictions.transform.dense.weight', 'cls.predictions.transform.dense.bias', 'cls.predictions.decoder.weight', 'cls.seq_relationship.weight', 'cls.seq_relationship.bias', 'cls.predictions.transform.LayerNorm.weight', 'cls.predictions.transform.LayerNorm.bias']\n",
|
||||
"- This IS expected if you are initializing BertForSequenceClassification from the checkpoint of a model trained on another task or with another architecture (e.g. initializing a BertForSequenceClassification model from a BertForPreTraining model).\n",
|
||||
"- This IS NOT expected if you are initializing BertForSequenceClassification from the checkpoint of a model that you expect to be exactly identical (initializing a BertForSequenceClassification model from a BertForSequenceClassification model).\n",
|
||||
"Some weights of BertForSequenceClassification were not initialized from the model checkpoint at ./bert and are newly initialized: ['classifier.weight', 'classifier.bias']\n",
|
||||
"You should probably TRAIN this model on a down-stream task to be able to use it for predictions and inference.\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"model = transformers.BertForSequenceClassification.from_pretrained(bert_model,num_labels=4).to(device)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"現在我們準備開始訓練了!由於 BERT 已經是預訓練模型,我們希望使用較小的學習率開始訓練,以免破壞初始權重。\n",
|
||||
"\n",
|
||||
"所有的繁重工作都由 `BertForSequenceClassification` 模型完成。當我們將訓練數據傳入模型時,它會返回損失值和輸入小批量數據的網絡輸出。我們使用損失值進行參數優化(`loss.backward()` 負責反向傳播),而使用 `out` 計算訓練準確率,方法是將獲得的標籤 `labs`(通過 `argmax` 計算)與預期的 `labels` 進行比較。\n",
|
||||
"\n",
|
||||
"為了控制訓練過程,我們會在多次迭代中累積損失值和準確率,並在每 `report_freq` 次訓練週期後打印出來。\n",
|
||||
"\n",
|
||||
"這次訓練可能需要相當長的時間,因此我們會限制迭代次數。\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 6,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"Loss = 1.1254194641113282, Accuracy = 0.585\n",
|
||||
"Loss = 0.6194715118408203, Accuracy = 0.83\n",
|
||||
"Loss = 0.46665248870849607, Accuracy = 0.8475\n",
|
||||
"Loss = 0.4309701919555664, Accuracy = 0.8575\n",
|
||||
"Loss = 0.35427074432373046, Accuracy = 0.8825\n",
|
||||
"Loss = 0.3306886291503906, Accuracy = 0.8975\n",
|
||||
"Loss = 0.30340143203735354, Accuracy = 0.8975\n",
|
||||
"Loss = 0.26139299392700194, Accuracy = 0.915\n",
|
||||
"Loss = 0.26708646774291994, Accuracy = 0.9225\n",
|
||||
"Loss = 0.3667240524291992, Accuracy = 0.8675\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"optimizer = torch.optim.Adam(model.parameters(), lr=2e-5)\n",
|
||||
"\n",
|
||||
"report_freq = 50\n",
|
||||
"iterations = 500 # make this larger to train for longer time!\n",
|
||||
"\n",
|
||||
"model.train()\n",
|
||||
"\n",
|
||||
"i,c = 0,0\n",
|
||||
"acc_loss = 0\n",
|
||||
"acc_acc = 0\n",
|
||||
"\n",
|
||||
"for labels,texts in train_loader:\n",
|
||||
" labels = labels.to(device)-1 # get labels in the range 0-3 \n",
|
||||
" texts = texts.to(device)\n",
|
||||
" loss, out = model(texts, labels=labels)[:2]\n",
|
||||
" labs = out.argmax(dim=1)\n",
|
||||
" acc = torch.mean((labs==labels).type(torch.float32))\n",
|
||||
" optimizer.zero_grad()\n",
|
||||
" loss.backward()\n",
|
||||
" optimizer.step()\n",
|
||||
" acc_loss += loss\n",
|
||||
" acc_acc += acc\n",
|
||||
" i+=1\n",
|
||||
" c+=1\n",
|
||||
" if i%report_freq==0:\n",
|
||||
" print(f\"Loss = {acc_loss.item()/c}, Accuracy = {acc_acc.item()/c}\")\n",
|
||||
" c = 0\n",
|
||||
" acc_loss = 0\n",
|
||||
" acc_acc = 0\n",
|
||||
" iterations-=1\n",
|
||||
" if not iterations:\n",
|
||||
" break"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"你可以看到(特別是當你增加迭代次數並等待足夠長的時間),BERT 分類器能夠提供相當不錯的準確度!這是因為 BERT 已經對語言的結構有相當深入的理解,我們只需要對最終的分類器進行微調即可。然而,由於 BERT 是一個大型模型,整個訓練過程需要花費很長的時間,並且需要強大的計算能力!(GPU,最好是多個 GPU)。\n",
|
||||
"\n",
|
||||
"> **注意:** 在我們的例子中,我們使用的是其中一個最小的預訓練 BERT 模型。還有更大的模型可能會帶來更好的結果。\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## 評估模型表現\n",
|
||||
"\n",
|
||||
"現在我們可以在測試數據集上評估模型的表現。評估的流程與訓練流程非常相似,但我們不能忘記透過呼叫 `model.eval()` 將模型切換到評估模式。\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 10,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"Final accuracy: 0.9047029702970297\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"model.eval()\n",
|
||||
"iterations = 100\n",
|
||||
"acc = 0\n",
|
||||
"i = 0\n",
|
||||
"for labels,texts in test_loader:\n",
|
||||
" labels = labels.to(device)-1 \n",
|
||||
" texts = texts.to(device)\n",
|
||||
" _, out = model(texts, labels=labels)[:2]\n",
|
||||
" labs = out.argmax(dim=1)\n",
|
||||
" acc += torch.mean((labs==labels).type(torch.float32))\n",
|
||||
" i+=1\n",
|
||||
" if i>iterations: break\n",
|
||||
" \n",
|
||||
"print(f\"Final accuracy: {acc.item()/i}\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## 重點\n",
|
||||
"\n",
|
||||
"在本單元中,我們已經看到如何輕鬆地從 **transformers** 庫中取用預訓練的語言模型,並將其調整至我們的文本分類任務。同樣地,BERT 模型也可以用於實體抽取、問題回答以及其他 NLP 任務。\n",
|
||||
"\n",
|
||||
"Transformer 模型代表了 NLP 領域的最新技術,在大多數情況下,當你開始嘗試實現自定義 NLP 解決方案時,它應該是首選。然而,如果你希望構建更高級的神經模型,理解本模組中討論的循環神經網絡的基本原理是非常重要的。\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"\n---\n\n**免責聲明**: \n本文件已使用人工智能翻譯服務 [Co-op Translator](https://github.com/Azure/co-op-translator) 進行翻譯。雖然我們致力於提供準確的翻譯,但請注意,自動翻譯可能包含錯誤或不準確之處。原始語言的文件應被視為權威來源。對於重要資訊,建議使用專業人工翻譯。我們對因使用此翻譯而引起的任何誤解或錯誤解釋概不負責。\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"kernelspec": {
|
||||
"display_name": "py37_pytorch",
|
||||
"language": "python",
|
||||
"name": "conda-env-py37_pytorch-py"
|
||||
},
|
||||
"language_info": {
|
||||
"codemirror_mode": {
|
||||
"name": "ipython",
|
||||
"version": 3
|
||||
},
|
||||
"file_extension": ".py",
|
||||
"mimetype": "text/x-python",
|
||||
"name": "python",
|
||||
"nbconvert_exporter": "python",
|
||||
"pygments_lexer": "ipython3",
|
||||
"version": "3.7.7"
|
||||
},
|
||||
"coopTranslator": {
|
||||
"original_hash": "753865967678a92dbce7d7efbd36d980",
|
||||
"translation_date": "2025-08-31T10:40:24+00:00",
|
||||
"source_file": "lessons/5-NLP/18-Transformers/TransformersPyTorch.ipynb",
|
||||
"language_code": "hk"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 4
|
||||
}
|
||||
|
|
@ -0,0 +1,819 @@
|
|||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"# 注意力機制與Transformer模型\n",
|
||||
"\n",
|
||||
"循環神經網絡的一個主要缺點是序列中的所有詞語對結果的影響力相同。這導致標準的LSTM編碼器-解碼器模型在處理序列到序列任務(例如命名實體識別和機器翻譯)時表現不佳。實際上,輸入序列中的某些特定詞語通常比其他詞語對輸出序列的影響更大。\n",
|
||||
"\n",
|
||||
"以序列到序列模型(例如機器翻譯)為例。這種模型由兩個循環神經網絡實現,其中一個網絡(**編碼器**)將輸入序列壓縮成隱藏狀態,另一個網絡(**解碼器**)將隱藏狀態展開成翻譯結果。這種方法的問題在於,網絡的最終狀態很難記住句子的開頭部分,因此在處理長句子時模型的質量會下降。\n",
|
||||
"\n",
|
||||
"**注意力機制**提供了一種方法,能夠對每個輸入向量在RNN輸出預測中的上下文影響進行加權。其實現方式是通過在輸入RNN的中間狀態和輸出RNN之間創建捷徑。在生成輸出符號$y_t$時,我們會考慮所有輸入隱藏狀態$h_i$,並賦予不同的權重係數$\\alpha_{t,i}$。\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"*帶有加性注意力機制的編碼器-解碼器模型,來自[Bahdanau et al., 2015](https://arxiv.org/pdf/1409.0473.pdf),引用自[這篇博客文章](https://lilianweng.github.io/lil-log/2018/06/24/attention-attention.html)*\n",
|
||||
"\n",
|
||||
"注意力矩陣$\\{\\alpha_{i,j}\\}$表示某些輸入詞語在生成輸出序列中的某個詞語時所起的作用程度。以下是這樣一個矩陣的示例:\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"*圖片取自[Bahdanau et al., 2015](https://arxiv.org/pdf/1409.0473.pdf)(圖3)*\n",
|
||||
"\n",
|
||||
"注意力機制是目前或接近目前自然語言處理領域的最先進技術的核心。儘管添加注意力機制會大幅增加模型參數的數量,但這也導致了RNN的擴展問題。RNN擴展的一個關鍵限制是模型的循環特性使得批量處理和訓練並行化變得困難。在RNN中,序列的每個元素都需要按順序處理,這意味著它無法輕易並行化。\n",
|
||||
"\n",
|
||||
"注意力機制的採用結合了這一限制,促成了如今最先進的Transformer模型的誕生,這些模型包括BERT和OpenGPT3等。\n",
|
||||
"\n",
|
||||
"## Transformer模型\n",
|
||||
"\n",
|
||||
"與將每次預測的上下文傳遞到下一個評估步驟不同,**Transformer模型**使用**位置編碼**和**注意力**來捕捉給定輸入在指定文本窗口內的上下文。下圖展示了位置編碼與注意力如何在給定窗口內捕捉上下文。\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"由於每個輸入位置可以獨立映射到每個輸出位置,Transformer模型比RNN更容易並行化,這使得能夠構建更大、更具表達力的語言模型。每個注意力頭可以用來學習詞語之間的不同關係,從而改善下游的自然語言處理任務。\n",
|
||||
"\n",
|
||||
"## 構建簡單的Transformer模型\n",
|
||||
"\n",
|
||||
"Keras並不包含內建的Transformer層,但我們可以自己構建。與之前一樣,我們將專注於AG News數據集的文本分類,但值得一提的是,Transformer模型在更困難的自然語言處理任務中表現最佳。\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 1,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import tensorflow as tf\n",
|
||||
"from tensorflow import keras\n",
|
||||
"import tensorflow_datasets as tfds\n",
|
||||
"import numpy as np\n",
|
||||
"\n",
|
||||
"ds_train, ds_test = tfds.load('ag_news_subset').values()\n",
|
||||
"\n",
|
||||
"def extract_text(x):\n",
|
||||
" return x['title']+' '+x['description']\n",
|
||||
"\n",
|
||||
"def tupelize(x):\n",
|
||||
" return (extract_text(x),x['label'])"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"新的 Keras 層應該繼承 `Layer` 類別,並實現 `call` 方法。我們先從 **位置嵌入** 層開始。我們將使用[官方 Keras 文件中的一些代碼](https://keras.io/examples/nlp/text_classification_with_transformer/)。我們假設我們將所有輸入序列填充到長度 `maxlen`。\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 2,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"class TokenAndPositionEmbedding(keras.layers.Layer):\n",
|
||||
" def __init__(self, maxlen, vocab_size, embed_dim):\n",
|
||||
" super(TokenAndPositionEmbedding, self).__init__()\n",
|
||||
" self.token_emb = keras.layers.Embedding(input_dim=vocab_size, output_dim=embed_dim)\n",
|
||||
" self.pos_emb = keras.layers.Embedding(input_dim=maxlen, output_dim=embed_dim)\n",
|
||||
" self.maxlen = maxlen\n",
|
||||
"\n",
|
||||
" def call(self, x):\n",
|
||||
" maxlen = self.maxlen\n",
|
||||
" positions = tf.range(start=0, limit=maxlen, delta=1)\n",
|
||||
" positions = self.pos_emb(positions)\n",
|
||||
" x = self.token_emb(x)\n",
|
||||
" return x+positions"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"這一層包含兩個 `Embedding` 層:一個用於嵌入標記(以我們之前討論過的方式),另一個用於嵌入標記的位置。標記位置是通過使用 `tf.range` 從 0 到 `maxlen` 創建的一個自然數序列,然後傳遞到嵌入層。兩個生成的嵌入向量隨後相加,產生輸入的帶位置嵌入的表示,其形狀為 `maxlen`$\\times$`embed_dim`。\n",
|
||||
"\n",
|
||||
"現在,我們來實現 transformer 區塊。它將接收之前定義的嵌入層的輸出:\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 模型:\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 模型\n",
|
||||
"\n",
|
||||
"**BERT**(Bidirectional Encoder Representations from Transformers,雙向編碼器表示)是一個非常大型的多層 Transformer 網絡,*BERT-base* 有 12 層,而 *BERT-large* 則有 24 層。該模型首先在大規模文本數據(維基百科 + 書籍)上進行無監督訓練(預測句子中被遮蔽的詞語)。在預訓練過程中,模型吸收了大量的語言理解能力,然後可以通過微調其他數據集來加以利用。這個過程被稱為 **遷移學習**。\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"Transformer 架構有許多變體,包括 BERT、DistilBERT、BigBird、OpenGPT3 等,這些模型都可以進行微調。\n",
|
||||
"\n",
|
||||
"現在讓我們看看如何使用預訓練的 BERT 模型來解決我們傳統的序列分類問題。我們將借用[官方文檔](https://www.tensorflow.org/text/tutorials/classify_text_with_bert)中的一些想法和代碼。\n",
|
||||
"\n",
|
||||
"為了加載預訓練模型,我們將使用 **Tensorflow hub**。首先,讓我們加載 BERT 專用的向量化工具:\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 1,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"ename": "ModuleNotFoundError",
|
||||
"evalue": "No module named 'tensorflow_text'",
|
||||
"output_type": "error",
|
||||
"traceback": [
|
||||
"\u001b[1;31m---------------------------------------------------------------------------\u001b[0m",
|
||||
"\u001b[1;31mModuleNotFoundError\u001b[0m Traceback (most recent call last)",
|
||||
"\u001b[1;32m~\\AppData\\Local\\Temp/ipykernel_41180/4216669875.py\u001b[0m in \u001b[0;36m<module>\u001b[1;34m\u001b[0m\n\u001b[1;32m----> 1\u001b[1;33m \u001b[1;32mimport\u001b[0m \u001b[0mtensorflow_text\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m\u001b[0;32m 2\u001b[0m \u001b[1;32mimport\u001b[0m \u001b[0mtensorflow_hub\u001b[0m \u001b[1;32mas\u001b[0m \u001b[0mhub\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 3\u001b[0m \u001b[0mvectorizer\u001b[0m \u001b[1;33m=\u001b[0m \u001b[0mhub\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mKerasLayer\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;34m'https://tfhub.dev/tensorflow/bert_en_uncased_preprocess/3'\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n",
|
||||
"\u001b[1;31mModuleNotFoundError\u001b[0m: No module named 'tensorflow_text'"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"import tensorflow_text \n",
|
||||
"import tensorflow_hub as hub\n",
|
||||
"vectorizer = hub.KerasLayer('https://tfhub.dev/tensorflow/bert_en_uncased_preprocess/3')"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 7,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"{'input_type_ids': <tf.Tensor: shape=(1, 128), dtype=int32, numpy=\n",
|
||||
" array([[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n",
|
||||
" 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n",
|
||||
" 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n",
|
||||
" 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n",
|
||||
" 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n",
|
||||
" 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]],\n",
|
||||
" dtype=int32)>,\n",
|
||||
" 'input_word_ids': <tf.Tensor: shape=(1, 128), dtype=int32, numpy=\n",
|
||||
" array([[ 101, 1045, 2293, 19081, 102, 0, 0, 0, 0,\n",
|
||||
" 0, 0, 0, 0, 0, 0, 0, 0, 0,\n",
|
||||
" 0, 0, 0, 0, 0, 0, 0, 0, 0,\n",
|
||||
" 0, 0, 0, 0, 0, 0, 0, 0, 0,\n",
|
||||
" 0, 0, 0, 0, 0, 0, 0, 0, 0,\n",
|
||||
" 0, 0, 0, 0, 0, 0, 0, 0, 0,\n",
|
||||
" 0, 0, 0, 0, 0, 0, 0, 0, 0,\n",
|
||||
" 0, 0, 0, 0, 0, 0, 0, 0, 0,\n",
|
||||
" 0, 0, 0, 0, 0, 0, 0, 0, 0,\n",
|
||||
" 0, 0, 0, 0, 0, 0, 0, 0, 0,\n",
|
||||
" 0, 0, 0, 0, 0, 0, 0, 0, 0,\n",
|
||||
" 0, 0, 0, 0, 0, 0, 0, 0, 0,\n",
|
||||
" 0, 0, 0, 0, 0, 0, 0, 0, 0,\n",
|
||||
" 0, 0, 0, 0, 0, 0, 0, 0, 0,\n",
|
||||
" 0, 0]], dtype=int32)>,\n",
|
||||
" 'input_mask': <tf.Tensor: shape=(1, 128), dtype=int32, numpy=\n",
|
||||
" array([[1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n",
|
||||
" 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n",
|
||||
" 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n",
|
||||
" 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n",
|
||||
" 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n",
|
||||
" 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]],\n",
|
||||
" dtype=int32)>}"
|
||||
]
|
||||
},
|
||||
"execution_count": 7,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"vectorizer(['I love transformers'])"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"使用與原始網絡訓練時相同的向量化工具是非常重要的。此外,BERT 向量化工具會返回三個組件:\n",
|
||||
"* `input_word_ids`,這是一個輸入句子的標記編號序列\n",
|
||||
"* `input_mask`,顯示序列中哪些部分包含實際輸入,哪些部分是填充。這與 `Masking` 層生成的遮罩類似\n",
|
||||
"* `input_type_ids` 用於語言建模任務,允許在一個序列中指定兩個輸入句子。\n",
|
||||
"\n",
|
||||
"然後,我們可以實例化 BERT 特徵提取器:\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 8,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"bert = hub.KerasLayer('https://tfhub.dev/tensorflow/small_bert/bert_en_uncased_L-4_H-128_A-2/1')"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 9,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"pooled_output -> (1, 128)\n",
|
||||
"encoder_outputs -> 4\n",
|
||||
"sequence_output -> (1, 128, 128)\n",
|
||||
"default -> (1, 128)\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"z = bert(vectorizer(['I love transformers']))\n",
|
||||
"for i,x in z.items():\n",
|
||||
" print(f\"{i} -> { len(x) if isinstance(x, list) else x.shape }\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"所以,BERT 層會返回一些有用的結果:\n",
|
||||
"* `pooled_output` 是通過平均序列中所有 token 的結果。你可以將其視為整個網絡的智能語義嵌入。它等同於我們之前模型中的 `GlobalAveragePooling1D` 層的輸出。\n",
|
||||
"* `sequence_output` 是最後一層 transformer 的輸出(對應於我們上面模型中的 `TransformerBlock` 的輸出)。\n",
|
||||
"* `encoder_outputs` 是所有 transformer 層的輸出。由於我們載入了 4 層的 BERT 模型(從名稱中包含的 `4_H` 你可能已經猜到),它有 4 個張量。最後一個張量與 `sequence_output` 相同。\n",
|
||||
"\n",
|
||||
"現在我們將定義端到端的分類模型。我們會使用*函數式模型定義*,即定義模型輸入,然後提供一系列表達式來計算其輸出。我們還會將 BERT 模型的權重設置為不可訓練,只訓練最終的分類器:\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 10,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"Model: \"model\"\n",
|
||||
"__________________________________________________________________________________________________\n",
|
||||
"Layer (type) Output Shape Param # Connected to \n",
|
||||
"==================================================================================================\n",
|
||||
"input_1 (InputLayer) [(None,)] 0 \n",
|
||||
"__________________________________________________________________________________________________\n",
|
||||
"keras_layer (KerasLayer) {'input_type_ids': ( 0 input_1[0][0] \n",
|
||||
"__________________________________________________________________________________________________\n",
|
||||
"keras_layer_1 (KerasLayer) {'pooled_output': (N 4782465 keras_layer[0][0] \n",
|
||||
" keras_layer[0][1] \n",
|
||||
" keras_layer[0][2] \n",
|
||||
"__________________________________________________________________________________________________\n",
|
||||
"dropout_4 (Dropout) (None, 128) 0 keras_layer_1[0][5] \n",
|
||||
"__________________________________________________________________________________________________\n",
|
||||
"dense_4 (Dense) (None, 4) 516 dropout_4[0][0] \n",
|
||||
"==================================================================================================\n",
|
||||
"Total params: 4,782,981\n",
|
||||
"Trainable params: 516\n",
|
||||
"Non-trainable params: 4,782,465\n",
|
||||
"__________________________________________________________________________________________________\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"inp = keras.Input(shape=(),dtype=tf.string)\n",
|
||||
"x = vectorizer(inp)\n",
|
||||
"x = bert(x)\n",
|
||||
"x = keras.layers.Dropout(0.1)(x['pooled_output'])\n",
|
||||
"out = keras.layers.Dense(4,activation='softmax')(x)\n",
|
||||
"model = keras.models.Model(inp,out)\n",
|
||||
"bert.trainable = False\n",
|
||||
"model.summary()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 11,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"938/938 [==============================] - 528s 559ms/step - loss: 0.8056 - acc: 0.6983 - val_loss: 0.5953 - val_acc: 0.7888\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"<tensorflow.python.keras.callbacks.History at 0x7f9bb1e36d00>"
|
||||
]
|
||||
},
|
||||
"execution_count": 11,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"model.compile(loss='sparse_categorical_crossentropy',metrics=['acc'], optimizer='adam')\n",
|
||||
"model.fit(ds_train.map(tupelize).batch(128),validation_data=ds_test.map(tupelize).batch(128))"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"儘管可訓練的參數很少,但過程仍然相當緩慢,因為 BERT 特徵提取器的計算量很大。看起來我們無法達到合理的準確度,可能是因為訓練不足,或者模型參數不足。\n",
|
||||
"\n",
|
||||
"讓我們嘗試解凍 BERT 的權重並一起訓練。這需要非常小的學習率,還需要更謹慎的訓練策略,包括使用 **warmup** 和 **AdamW** 優化器。我們將使用 `tf-models-official` 套件來創建優化器:\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 12,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"Model: \"model\"\n",
|
||||
"__________________________________________________________________________________________________\n",
|
||||
"Layer (type) Output Shape Param # Connected to \n",
|
||||
"==================================================================================================\n",
|
||||
"input_1 (InputLayer) [(None,)] 0 \n",
|
||||
"__________________________________________________________________________________________________\n",
|
||||
"keras_layer (KerasLayer) {'input_type_ids': ( 0 input_1[0][0] \n",
|
||||
"__________________________________________________________________________________________________\n",
|
||||
"keras_layer_1 (KerasLayer) {'pooled_output': (N 4782465 keras_layer[0][0] \n",
|
||||
" keras_layer[0][1] \n",
|
||||
" keras_layer[0][2] \n",
|
||||
"__________________________________________________________________________________________________\n",
|
||||
"dropout_4 (Dropout) (None, 128) 0 keras_layer_1[0][5] \n",
|
||||
"__________________________________________________________________________________________________\n",
|
||||
"dense_4 (Dense) (None, 4) 516 dropout_4[0][0] \n",
|
||||
"==================================================================================================\n",
|
||||
"Total params: 4,782,981\n",
|
||||
"Trainable params: 4,782,980\n",
|
||||
"Non-trainable params: 1\n",
|
||||
"__________________________________________________________________________________________________\n",
|
||||
"938/938 [==============================] - 629s 664ms/step - loss: 0.6344 - acc: 0.7658 - val_loss: 0.4876 - val_acc: 0.8247\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"<tensorflow.python.keras.callbacks.History at 0x7f9bb0bd0070>"
|
||||
]
|
||||
},
|
||||
"execution_count": 12,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"from official.nlp import optimization \n",
|
||||
"bert.trainable=True\n",
|
||||
"model.summary()\n",
|
||||
"epochs = 3\n",
|
||||
"opt = optimization.create_optimizer(\n",
|
||||
" init_lr=3e-5,\n",
|
||||
" num_train_steps=epochs*len(ds_train),\n",
|
||||
" num_warmup_steps=0.1*epochs*len(ds_train),\n",
|
||||
" optimizer_type='adamw')\n",
|
||||
"\n",
|
||||
"model.compile(loss='sparse_categorical_crossentropy',metrics=['acc'], optimizer=opt)\n",
|
||||
"model.fit(ds_train.map(tupelize).batch(128),validation_data=ds_test.map(tupelize).batch(128))"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"如你所見,訓練過程相當緩慢——但你可能想嘗試進行幾個訓練週期(5-10次),看看是否能夠與我們之前使用的方法相比,獲得最佳結果。\n",
|
||||
"\n",
|
||||
"## Huggingface Transformers 庫\n",
|
||||
"\n",
|
||||
"另一種非常常見(且稍微簡單)的使用 Transformer 模型的方法是 [HuggingFace 套件](https://github.com/huggingface/),它為不同的 NLP 任務提供了簡單的構建模塊。此套件同時支援 Tensorflow 和 PyTorch,後者是另一個非常受歡迎的神經網絡框架。\n",
|
||||
"\n",
|
||||
"> **注意**:如果你對了解 Transformers 庫的運作方式不感興趣——你可以跳到筆記本的最後部分,因為你不會看到任何與我們之前所做的有實質性不同的內容。我們將重複使用不同的庫和更大的模型來訓練 BERT 模型的相同步驟。因此,這個過程涉及一些相當長的訓練時間,所以你可能只想瀏覽一下程式碼。\n",
|
||||
"\n",
|
||||
"讓我們看看如何使用 [Huggingface Transformers](http://huggingface.co) 解決我們的問題。\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"首先,我們需要選擇要使用的模型。除了內建的模型之外,Huggingface 還有一個[線上模型庫](https://huggingface.co/models),社群提供了許多預訓練模型。只需提供模型名稱,就可以載入並使用這些模型。所有模型所需的二進制檔案會自動下載。\n",
|
||||
"\n",
|
||||
"有時候你可能需要載入自己的模型,這時可以指定包含所有相關檔案的目錄,包括 tokenizer 的參數、`config.json` 文件(包含模型參數)、二進制權重等。\n",
|
||||
"\n",
|
||||
"透過模型名稱,我們可以初始化模型和 tokenizer。讓我們先從 tokenizer 開始:\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 2,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import transformers\n",
|
||||
"\n",
|
||||
"# To load the model from Internet repository using model name. \n",
|
||||
"# Use this if you are running from your own copy of the notebooks\n",
|
||||
"bert_model = 'bert-base-uncased' \n",
|
||||
"\n",
|
||||
"# To load the model from the directory on disk. Use this for Microsoft Learn module, because we have\n",
|
||||
"# prepared all required files for you.\n",
|
||||
"#bert_model = './bert'\n",
|
||||
"\n",
|
||||
"tokenizer = transformers.BertTokenizer.from_pretrained(bert_model)\n",
|
||||
"\n",
|
||||
"MAX_SEQ_LEN = 128\n",
|
||||
"PAD_INDEX = tokenizer.convert_tokens_to_ids(tokenizer.pad_token)\n",
|
||||
"UNK_INDEX = tokenizer.convert_tokens_to_ids(tokenizer.unk_token)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"`tokenizer` 對象包含可直接用於編碼文本的 `encode` 函數:\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 3,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"[101, 23435, 12314, 2003, 1037, 2307, 7705, 2005, 17953, 2361, 102]"
|
||||
]
|
||||
},
|
||||
"execution_count": 3,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"tokenizer.encode('Tensorflow is a great framework for NLP')"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"我們還可以使用分詞器以適合傳遞給模型的方式對序列進行編碼,即包括 `token_ids`、`input_mask` 等字段。我們還可以通過提供 `return_tensors='tf'` 參數來指定我們想要 Tensorflow 張量:\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 4,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"{'input_ids': <tf.Tensor: shape=(1, 5), dtype=int32, numpy=array([[ 101, 7592, 1010, 2045, 102]], dtype=int32)>, 'token_type_ids': <tf.Tensor: shape=(1, 5), dtype=int32, numpy=array([[0, 0, 0, 0, 0]], dtype=int32)>, 'attention_mask': <tf.Tensor: shape=(1, 5), dtype=int32, numpy=array([[1, 1, 1, 1, 1]], dtype=int32)>}"
|
||||
]
|
||||
},
|
||||
"execution_count": 4,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"tokenizer(['Hello, there'],return_tensors='tf')"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"在我們的案例中,我們將使用預訓練的 BERT 模型,名為 `bert-base-uncased`。*Uncased* 表示該模型對大小寫不敏感。\n",
|
||||
"\n",
|
||||
"在訓練模型時,我們需要提供已分詞的序列作為輸入,因此我們將設計數據處理管道。由於 `tokenizer.encode` 是一個 Python 函數,我們將採用與上一單元相同的方法,使用 `py_function` 來調用它:\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 31,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"def process(x):\n",
|
||||
" return tokenizer.encode(x.numpy().decode('utf-8'),return_tensors='tf',padding='max_length',max_length=MAX_SEQ_LEN,truncation=True)[0]\n",
|
||||
"\n",
|
||||
"def process_fn(x):\n",
|
||||
" s = x['title']+' '+x['description']\n",
|
||||
" e = tf.py_function(process,inp=[s],Tout=(tf.int32))\n",
|
||||
" e.set_shape(MAX_SEQ_LEN)\n",
|
||||
" return e,x['label']"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"現在我們可以使用 `BertForSequenceClassfication` 套件加載實際模型。這確保了我們的模型已經具備分類所需的架構,包括最終的分類器。你會看到一條警告訊息,指出最終分類器的權重尚未初始化,並且模型需要進行預訓練——這完全沒問題,因為這正是我們即將進行的操作!\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 32,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"model = transformers.TFBertForSequenceClassification.from_pretrained(bert_model,num_labels=4,output_attentions=False)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 33,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"Model: \"tf_bert_for_sequence_classification_1\"\n",
|
||||
"_________________________________________________________________\n",
|
||||
"Layer (type) Output Shape Param # \n",
|
||||
"=================================================================\n",
|
||||
"bert (TFBertMainLayer) multiple 109482240 \n",
|
||||
"_________________________________________________________________\n",
|
||||
"dropout_75 (Dropout) multiple 0 \n",
|
||||
"_________________________________________________________________\n",
|
||||
"classifier (Dense) multiple 3076 \n",
|
||||
"=================================================================\n",
|
||||
"Total params: 109,485,316\n",
|
||||
"Trainable params: 109,485,316\n",
|
||||
"Non-trainable params: 0\n",
|
||||
"_________________________________________________________________\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"model.summary()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"從 `summary()` 可以看到,該模型包含了接近 1.1 億個參數!假設我們想在相對較小的數據集上進行簡單的分類任務,我們可能不希望訓練 BERT 的基礎層:\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 34,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"Model: \"tf_bert_for_sequence_classification_1\"\n",
|
||||
"_________________________________________________________________\n",
|
||||
"Layer (type) Output Shape Param # \n",
|
||||
"=================================================================\n",
|
||||
"bert (TFBertMainLayer) multiple 109482240 \n",
|
||||
"_________________________________________________________________\n",
|
||||
"dropout_75 (Dropout) multiple 0 \n",
|
||||
"_________________________________________________________________\n",
|
||||
"classifier (Dense) multiple 3076 \n",
|
||||
"=================================================================\n",
|
||||
"Total params: 109,485,316\n",
|
||||
"Trainable params: 3,076\n",
|
||||
"Non-trainable params: 109,482,240\n",
|
||||
"_________________________________________________________________\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"model.layers[0].trainable = False\n",
|
||||
"model.summary()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"現在我們準備開始訓練!\n",
|
||||
"\n",
|
||||
"> **注意**:訓練完整規模的 BERT 模型可能會非常耗時!因此,我們只會訓練前 32 個批次。這只是為了展示模型訓練的設置方式。如果你有興趣嘗試完整規模的訓練,只需移除 `steps_per_epoch` 和 `validation_steps` 參數,然後準備耐心等待!\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 30,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"32/32 [==============================] - 142s 4s/step - loss: 1.3896 - acc: 0.2500 - val_loss: 1.3863 - val_acc: 0.2480\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"<tensorflow.python.keras.callbacks.History at 0x7f1d40a4b6a0>"
|
||||
]
|
||||
},
|
||||
"execution_count": 30,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"model.compile('adam','sparse_categorical_crossentropy',['acc'])\n",
|
||||
"tf.get_logger().setLevel('ERROR')\n",
|
||||
"model.fit(ds_train.map(process_fn).batch(32),validation_data=ds_test.map(process_fn).batch(32),steps_per_epoch=32,validation_steps=2)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"如果你增加迭代次數並耐心等待,並且訓練多個epoch,你可以預期BERT分類會給我們最好的準確率!這是因為BERT已經相當理解語言的結構,我們只需要微調最終的分類器。然而,由於BERT是一個大型模型,整個訓練過程需要很長時間,並且需要強大的計算能力!(GPU,最好是多於一個)。\n",
|
||||
"\n",
|
||||
"> **Note:** 在我們的例子中,我們使用的是其中一個最小的預訓練BERT模型。還有更大的模型可能會帶來更好的結果。\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## 重點\n",
|
||||
"\n",
|
||||
"在本單元中,我們探討了基於 **transformers** 的最新模型架構。我們已將其應用於文本分類任務,但同樣地,BERT 模型也可以用於實體抽取、問題回答以及其他自然語言處理任務。\n",
|
||||
"\n",
|
||||
"Transformer 模型代表了自然語言處理領域的最新技術,並且在大多數情況下,應該是您在實現自定義 NLP 解決方案時首先嘗試的選擇。然而,如果您希望構建更高級的神經網絡模型,理解本模組中討論的循環神經網絡的基本原理是非常重要的。\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": []
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"\n---\n\n**免責聲明**: \n本文件已使用人工智能翻譯服務 [Co-op Translator](https://github.com/Azure/co-op-translator) 進行翻譯。我們致力於提供準確的翻譯,但請注意,自動翻譯可能包含錯誤或不準確之處。應以原文文件作為權威來源。對於關鍵資訊,建議尋求專業人工翻譯。我們對因使用此翻譯而引起的任何誤解或誤釋不承擔責任。\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"interpreter": {
|
||||
"hash": "0cb620c6d4b9f7a635928804c26cf22403d89d98d79684e4529119355ee6d5a5"
|
||||
},
|
||||
"kernelspec": {
|
||||
"display_name": "py38_tensorflow",
|
||||
"language": "python",
|
||||
"name": "python3"
|
||||
},
|
||||
"language_info": {
|
||||
"codemirror_mode": {
|
||||
"name": "ipython",
|
||||
"version": 3
|
||||
},
|
||||
"file_extension": ".py",
|
||||
"mimetype": "text/x-python",
|
||||
"name": "python",
|
||||
"nbconvert_exporter": "python",
|
||||
"pygments_lexer": "ipython3",
|
||||
"version": "3.8.12"
|
||||
},
|
||||
"coopTranslator": {
|
||||
"original_hash": "ab59c532409774988ab875f2260e8e53",
|
||||
"translation_date": "2025-08-31T10:43:28+00:00",
|
||||
"source_file": "lessons/5-NLP/18-Transformers/TransformersTF.ipynb",
|
||||
"language_code": "hk"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 4
|
||||
}
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
# 作業:Transformers
|
||||
|
||||
在 HuggingFace 上試驗 Transformers!嘗試使用他們提供的腳本來操作網站上各種可用的模型:https://huggingface.co/docs/transformers/run_scripts。嘗試使用他們的一個數據集,然後從本課程或 Kaggle 匯入您自己的數據集,看看是否能生成有趣的文本。製作一個筆記本來記錄您的發現。
|
||||
|
||||
**免責聲明**:
|
||||
本文件已使用人工智能翻譯服務 [Co-op Translator](https://github.com/Azure/co-op-translator) 進行翻譯。我們致力於提供準確的翻譯,但請注意,自動翻譯可能包含錯誤或不準確之處。應以原文文件作為權威來源。對於關鍵資訊,建議尋求專業人工翻譯。我們對因使用此翻譯而引起的任何誤解或錯誤解釋概不負責。
|
||||
|
|
@ -0,0 +1,492 @@
|
|||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"# 命名實體識別 (NER)\n",
|
||||
"\n",
|
||||
"此筆記本來自 [AI for Beginners Curriculum](http://aka.ms/ai-beginners)。\n",
|
||||
"\n",
|
||||
"在這個例子中,我們將學習如何在 [Annotated Corpus for Named Entity Recognition](https://www.kaggle.com/datasets/abhinavwalia95/entity-annotated-corpus) 資料集上訓練 NER 模型。在開始之前,請下載 [ner_dataset.csv](https://www.kaggle.com/datasets/abhinavwalia95/entity-annotated-corpus?resource=download&select=ner_dataset.csv) 文件到當前目錄。\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 62,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import pandas as pd\n",
|
||||
"from tensorflow import keras\n",
|
||||
"import numpy as np"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## 準備數據集\n",
|
||||
"\n",
|
||||
"我們將從將數據集讀入一個 dataframe 開始。如果你想了解更多有關使用 Pandas 的資訊,可以參考我們 [初學者數據科學](http://aka.ms/datascience-beginners) 中的 [數據處理課程](https://github.com/microsoft/Data-Science-For-Beginners/tree/main/2-Working-With-Data/07-python)。\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 3,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"text/html": [
|
||||
"<div>\n",
|
||||
"<style scoped>\n",
|
||||
" .dataframe tbody tr th:only-of-type {\n",
|
||||
" vertical-align: middle;\n",
|
||||
" }\n",
|
||||
"\n",
|
||||
" .dataframe tbody tr th {\n",
|
||||
" vertical-align: top;\n",
|
||||
" }\n",
|
||||
"\n",
|
||||
" .dataframe thead th {\n",
|
||||
" text-align: right;\n",
|
||||
" }\n",
|
||||
"</style>\n",
|
||||
"<table border=\"1\" class=\"dataframe\">\n",
|
||||
" <thead>\n",
|
||||
" <tr style=\"text-align: right;\">\n",
|
||||
" <th></th>\n",
|
||||
" <th>Sentence #</th>\n",
|
||||
" <th>Word</th>\n",
|
||||
" <th>POS</th>\n",
|
||||
" <th>Tag</th>\n",
|
||||
" </tr>\n",
|
||||
" </thead>\n",
|
||||
" <tbody>\n",
|
||||
" <tr>\n",
|
||||
" <th>0</th>\n",
|
||||
" <td>Sentence: 1</td>\n",
|
||||
" <td>Thousands</td>\n",
|
||||
" <td>NNS</td>\n",
|
||||
" <td>O</td>\n",
|
||||
" </tr>\n",
|
||||
" <tr>\n",
|
||||
" <th>1</th>\n",
|
||||
" <td>NaN</td>\n",
|
||||
" <td>of</td>\n",
|
||||
" <td>IN</td>\n",
|
||||
" <td>O</td>\n",
|
||||
" </tr>\n",
|
||||
" <tr>\n",
|
||||
" <th>2</th>\n",
|
||||
" <td>NaN</td>\n",
|
||||
" <td>demonstrators</td>\n",
|
||||
" <td>NNS</td>\n",
|
||||
" <td>O</td>\n",
|
||||
" </tr>\n",
|
||||
" <tr>\n",
|
||||
" <th>3</th>\n",
|
||||
" <td>NaN</td>\n",
|
||||
" <td>have</td>\n",
|
||||
" <td>VBP</td>\n",
|
||||
" <td>O</td>\n",
|
||||
" </tr>\n",
|
||||
" <tr>\n",
|
||||
" <th>4</th>\n",
|
||||
" <td>NaN</td>\n",
|
||||
" <td>marched</td>\n",
|
||||
" <td>VBN</td>\n",
|
||||
" <td>O</td>\n",
|
||||
" </tr>\n",
|
||||
" </tbody>\n",
|
||||
"</table>\n",
|
||||
"</div>"
|
||||
],
|
||||
"text/plain": [
|
||||
" Sentence # Word POS Tag\n",
|
||||
"0 Sentence: 1 Thousands NNS O\n",
|
||||
"1 NaN of IN O\n",
|
||||
"2 NaN demonstrators NNS O\n",
|
||||
"3 NaN have VBP O\n",
|
||||
"4 NaN marched VBN O"
|
||||
]
|
||||
},
|
||||
"execution_count": 3,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"df = pd.read_csv('ner_dataset.csv',encoding='unicode-escape')\n",
|
||||
"df.head()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"讓我們獲取獨特的標籤並創建查詢字典,以便我們可以用來將標籤轉換為類別編號:\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 4,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"array(['O', 'B-geo', 'B-gpe', 'B-per', 'I-geo', 'B-org', 'I-org', 'B-tim',\n",
|
||||
" 'B-art', 'I-art', 'I-per', 'I-gpe', 'I-tim', 'B-nat', 'B-eve',\n",
|
||||
" 'I-eve', 'I-nat'], dtype=object)"
|
||||
]
|
||||
},
|
||||
"execution_count": 4,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"tags = df.Tag.unique()\n",
|
||||
"tags"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 8,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"'O'"
|
||||
]
|
||||
},
|
||||
"execution_count": 8,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"id2tag = dict(enumerate(tags))\n",
|
||||
"tag2id = { v : k for k,v in id2tag.items() }\n",
|
||||
"\n",
|
||||
"id2tag[0]"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"現在我們需要對詞彙做同樣的處理。為了簡化,我們將在不考慮詞頻的情況下創建詞彙;在現實中,你可能會想使用 Keras 向量化工具,並限制詞彙的數量。\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 14,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"vocab = set(df['Word'].apply(lambda x: x.lower()))\n",
|
||||
"id2word = { i+1 : v for i,v in enumerate(vocab) }\n",
|
||||
"id2word[0] = '<UNK>'\n",
|
||||
"vocab.add('<UNK>')\n",
|
||||
"word2id = { v : k for k,v in id2word.items() }"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"我們需要建立一個句子數據集用於訓練。讓我們遍歷原始數據集,並將所有單個句子分成 `X`(單詞列表)和 `Y`(標記列表):\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 41,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"X,Y = [],[]\n",
|
||||
"s,t = [],[]\n",
|
||||
"for i,row in df[['Sentence #','Word','Tag']].iterrows():\n",
|
||||
" if pd.isna(row['Sentence #']):\n",
|
||||
" s.append(row['Word'])\n",
|
||||
" t.append(row['Tag'])\n",
|
||||
" else:\n",
|
||||
" if len(s)>0:\n",
|
||||
" X.append(s)\n",
|
||||
" Y.append(t)\n",
|
||||
" s,t = [row['Word']],[row['Tag']]\n",
|
||||
"X.append(s)\n",
|
||||
"Y.append(t)\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": []
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 93,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"([10386,\n",
|
||||
" 23515,\n",
|
||||
" 4134,\n",
|
||||
" 29620,\n",
|
||||
" 7954,\n",
|
||||
" 13583,\n",
|
||||
" 21193,\n",
|
||||
" 12222,\n",
|
||||
" 27322,\n",
|
||||
" 18258,\n",
|
||||
" 5815,\n",
|
||||
" 15880,\n",
|
||||
" 5355,\n",
|
||||
" 25242,\n",
|
||||
" 31327,\n",
|
||||
" 18258,\n",
|
||||
" 27067,\n",
|
||||
" 23515,\n",
|
||||
" 26444,\n",
|
||||
" 14412,\n",
|
||||
" 358,\n",
|
||||
" 26551,\n",
|
||||
" 5011,\n",
|
||||
" 30558],\n",
|
||||
" [0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0])"
|
||||
]
|
||||
},
|
||||
"execution_count": 93,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"def vectorize(seq):\n",
|
||||
" return [word2id[x.lower()] for x in seq]\n",
|
||||
"\n",
|
||||
"def tagify(seq):\n",
|
||||
" return [tag2id[x] for x in seq]\n",
|
||||
"\n",
|
||||
"Xv = list(map(vectorize,X))\n",
|
||||
"Yv = list(map(tagify,Y))\n",
|
||||
"\n",
|
||||
"Xv[0], Yv[0]"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"為了簡化,我們將所有句子用 0 代幣填充到最大長度。在現實生活中,我們可能會想使用更聰明的策略,僅在一個小批次內填充序列。\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 51,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"X_data = keras.preprocessing.sequence.pad_sequences(Xv,padding='post')\n",
|
||||
"Y_data = keras.preprocessing.sequence.pad_sequences(Yv,padding='post')"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## 定義標記分類網絡\n",
|
||||
"\n",
|
||||
"我們將使用兩層雙向 LSTM 網絡進行標記分類。為了將密集分類器應用於最後一層 LSTM 的每個輸出,我們會使用 `TimeDistributed` 結構,該結構會在每一步中將相同的密集層複製到 LSTM 的每個輸出:\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 94,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"Model: \"sequential_3\"\n",
|
||||
"_________________________________________________________________\n",
|
||||
" Layer (type) Output Shape Param # \n",
|
||||
"=================================================================\n",
|
||||
" embedding_4 (Embedding) (None, 104, 300) 9545400 \n",
|
||||
" \n",
|
||||
" bidirectional_6 (Bidirectio (None, 104, 200) 320800 \n",
|
||||
" nal) \n",
|
||||
" \n",
|
||||
" bidirectional_7 (Bidirectio (None, 104, 200) 240800 \n",
|
||||
" nal) \n",
|
||||
" \n",
|
||||
" time_distributed_3 (TimeDis (None, 104, 17) 3417 \n",
|
||||
" tributed) \n",
|
||||
" \n",
|
||||
"=================================================================\n",
|
||||
"Total params: 10,110,417\n",
|
||||
"Trainable params: 10,110,417\n",
|
||||
"Non-trainable params: 0\n",
|
||||
"_________________________________________________________________\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"maxlen = X_data.shape[1]\n",
|
||||
"vocab_size = len(vocab)\n",
|
||||
"num_tags = len(tags)\n",
|
||||
"model = keras.models.Sequential([\n",
|
||||
" keras.layers.Embedding(vocab_size, 300, input_length=maxlen),\n",
|
||||
" keras.layers.Bidirectional(keras.layers.LSTM(units=100, activation='tanh', return_sequences=True)),\n",
|
||||
" keras.layers.Bidirectional(keras.layers.LSTM(units=100, activation='tanh', return_sequences=True)),\n",
|
||||
" keras.layers.TimeDistributed(keras.layers.Dense(num_tags, activation='softmax'))\n",
|
||||
"])\n",
|
||||
"model.compile(loss='sparse_categorical_crossentropy',optimizer='adam',metrics=['acc'])\n",
|
||||
"model.summary()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"注意這裡,我們為數據集明確指定了 `maxlen`——如果我們希望網絡能夠處理可變長度的序列,那麼在定義網絡時需要更聰明一些。\n",
|
||||
"\n",
|
||||
"現在讓我們訓練模型。為了加快速度,我們只訓練一個 epoch,但你可以嘗試訓練更長的時間。此外,你可能希望將部分數據集分離作為訓練數據集,以觀察驗證準確率。\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 57,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"1499/1499 [==============================] - 740s 488ms/step - loss: 0.0667 - acc: 0.9841\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"<keras.callbacks.History at 0x16f0bb2a310>"
|
||||
]
|
||||
},
|
||||
"execution_count": 57,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"model.fit(X_data,Y_data)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## 測試結果\n",
|
||||
"\n",
|
||||
"現在讓我們看看實體識別模型在一個示例句子上的表現:\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 91,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"sent = 'John Smith went to Paris to attend a conference in cancer development institute'\n",
|
||||
"words = sent.lower().split()\n",
|
||||
"v = keras.preprocessing.sequence.pad_sequences([[word2id[x] for x in words]],padding='post',maxlen=maxlen)\n",
|
||||
"res = model(v)[0]"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 92,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"john -> B-per\n",
|
||||
"smith -> I-per\n",
|
||||
"went -> O\n",
|
||||
"to -> O\n",
|
||||
"paris -> B-geo\n",
|
||||
"to -> O\n",
|
||||
"attend -> O\n",
|
||||
"a -> O\n",
|
||||
"conference -> O\n",
|
||||
"in -> O\n",
|
||||
"cancer -> B-org\n",
|
||||
"development -> I-org\n",
|
||||
"institute -> I-org\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"r = np.argmax(res.numpy(),axis=1)\n",
|
||||
"for i,w in zip(r,words):\n",
|
||||
" print(f\"{w} -> {id2tag[i]}\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## 重點\n",
|
||||
"\n",
|
||||
"即使是簡單的 LSTM 模型,在命名實體識別(NER)方面也能展示出不錯的效果。然而,若要獲得更佳的結果,你可能需要使用大型的預訓練語言模型,例如 BERT。使用 Huggingface Transformers 庫來訓練 BERT 用於 NER 的方法已在[這裡](https://huggingface.co/course/chapter7/2?fw=pt)進行了說明。\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"\n---\n\n**免責聲明**: \n本文件已使用人工智能翻譯服務 [Co-op Translator](https://github.com/Azure/co-op-translator) 進行翻譯。我們致力於提供準確的翻譯,但請注意,自動翻譯可能包含錯誤或不準確之處。應以原文文件作為權威來源。對於關鍵資訊,建議尋求專業人工翻譯。我們對因使用此翻譯而引起的任何誤解或誤釋不承擔責任。\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"interpreter": {
|
||||
"hash": "16af2a8bbb083ea23e5e41c7f5787656b2ce26968575d8763f2c4b17f9cd711f"
|
||||
},
|
||||
"kernelspec": {
|
||||
"display_name": "Python 3.8.12 ('py38')",
|
||||
"language": "python",
|
||||
"name": "python3"
|
||||
},
|
||||
"language_info": {
|
||||
"codemirror_mode": {
|
||||
"name": "ipython",
|
||||
"version": 3
|
||||
},
|
||||
"file_extension": ".py",
|
||||
"mimetype": "text/x-python",
|
||||
"name": "python",
|
||||
"nbconvert_exporter": "python",
|
||||
"pygments_lexer": "ipython3",
|
||||
"version": "3.8.12"
|
||||
},
|
||||
"orig_nbformat": 4,
|
||||
"coopTranslator": {
|
||||
"original_hash": "254d25052dcca4ef84f59a05f2935bdc",
|
||||
"translation_date": "2025-08-31T10:45:44+00:00",
|
||||
"source_file": "lessons/5-NLP/19-NER/NER-TF.ipynb",
|
||||
"language_code": "hk"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 2
|
||||
}
|
||||
|
|
@ -0,0 +1,84 @@
|
|||
# 命名實體識別
|
||||
|
||||
到目前為止,我們主要集中在一個 NLP 任務——分類。然而,還有其他 NLP 任務可以通過神經網絡完成。其中一個任務是 **[命名實體識別](https://wikipedia.org/wiki/Named-entity_recognition)** (NER),它的目的是識別文本中的特定實體,例如地點、人物姓名、日期時間範圍、化學式等等。
|
||||
|
||||
## [課前測驗](https://ff-quizzes.netlify.app/en/ai/quiz/37)
|
||||
|
||||
## 使用 NER 的例子
|
||||
|
||||
假設你想開發一個自然語言聊天機器人,類似於 Amazon Alexa 或 Google Assistant。智能聊天機器人的工作方式是通過對輸入句子進行文本分類來*理解*用戶的需求。分類的結果是所謂的 **意圖**,它決定了聊天機器人應該執行什麼操作。
|
||||
|
||||
<img alt="Bot NER" src="../../../../../translated_images/zh-HK/bot-ner.4b09235dbb0ad275.webp" width="50%"/>
|
||||
|
||||
> 圖片由作者提供
|
||||
|
||||
然而,用戶可能會在短語中提供一些參數。例如,當詢問天氣時,她可能會指定地點或日期。機器人應該能夠理解這些實體,並在執行操作之前相應地填充參數槽。這正是 NER 的作用所在。
|
||||
|
||||
> ✅ 另一個例子是 [分析科學醫學論文](https://soshnikov.com/science/analyzing-medical-papers-with-azure-and-text-analytics-for-health/)。我們需要尋找的主要內容是特定的醫學術語,例如疾病和醫學物質。雖然少量疾病可能可以通過子字符串搜索提取,但更複雜的實體,例如化學化合物和藥物名稱,則需要更複雜的方法。
|
||||
|
||||
## NER 作為標記分類
|
||||
|
||||
NER 模型本質上是 **標記分類模型**,因為對於每個輸入標記,我們需要決定它是否屬於某個實體,如果屬於——它屬於哪個實體類別。
|
||||
|
||||
考慮以下論文標題:
|
||||
|
||||
**三尖瓣反流** 和 **碳酸鋰** **毒性** 在一名新生兒中。
|
||||
|
||||
其中的實體是:
|
||||
|
||||
* 三尖瓣反流是一種疾病 (`DIS`)
|
||||
* 碳酸鋰是一種化學物質 (`CHEM`)
|
||||
* 毒性也是一種疾病 (`DIS`)
|
||||
|
||||
注意,一個實體可能跨越多個標記。而且,如本例所示,我們需要區分兩個連續的實體。因此,通常為每個實體使用兩個類別——一個指定實體的第一個標記(通常使用 `B-` 前綴,表示 **開始**),另一個表示實體的延續部分(`I-`,表示 **內部標記**)。我們還使用 `O` 作為類別來表示所有 **其他** 標記。這種標記標籤方法被稱為 [BIO 標籤](https://en.wikipedia.org/wiki/Inside%E2%80%93outside%E2%80%93beginning_(tagging))(或 IOB)。標記後,我們的標題將如下所示:
|
||||
|
||||
標記 | 標籤
|
||||
------|-----
|
||||
三尖瓣 | B-DIS
|
||||
反流 | I-DIS
|
||||
和 | O
|
||||
碳酸 | B-CHEM
|
||||
鋰 | I-CHEM
|
||||
毒性 | B-DIS
|
||||
在 | O
|
||||
一名 | O
|
||||
新生兒 | O
|
||||
中 | O
|
||||
。 | O
|
||||
|
||||
由於我們需要在標記和類別之間建立一對一的對應關係,我們可以從這張圖中訓練一個最右側的 **多對多** 神經網絡模型:
|
||||
|
||||

|
||||
|
||||
> *圖片來自 [這篇博客文章](http://karpathy.github.io/2015/05/21/rnn-effectiveness/) 作者 [Andrej Karpathy](http://karpathy.github.io/)。NER 標記分類模型對應於圖片中最右側的網絡架構。*
|
||||
|
||||
## 訓練 NER 模型
|
||||
|
||||
由於 NER 模型本質上是標記分類模型,我們可以使用我們已經熟悉的 RNN 來完成這項任務。在這種情況下,每個循環網絡的塊都會返回標記 ID。以下範例筆記本展示了如何訓練 LSTM 進行標記分類。
|
||||
|
||||
## ✍️ 範例筆記本:NER
|
||||
|
||||
繼續學習以下筆記本:
|
||||
|
||||
* [使用 TensorFlow 的 NER](NER-TF.ipynb)
|
||||
|
||||
## 結論
|
||||
|
||||
NER 模型是一種 **標記分類模型**,這意味著它可以用於執行標記分類。這是一個 NLP 中非常常見的任務,幫助識別文本中的特定實體,包括地點、姓名、日期等。
|
||||
|
||||
## 🚀 挑戰
|
||||
|
||||
完成以下連結的作業,訓練一個醫學術語的命名實體識別模型,然後嘗試在不同的數據集上使用它。
|
||||
|
||||
## [課後測驗](https://ff-quizzes.netlify.app/en/ai/quiz/38)
|
||||
|
||||
## 回顧與自學
|
||||
|
||||
閱讀博客 [循環神經網絡的非凡效能](http://karpathy.github.io/2015/05/21/rnn-effectiveness/),並按照文章中的進一步閱讀部分加深你的知識。
|
||||
|
||||
## [作業](lab/README.md)
|
||||
|
||||
在本課的作業中,你需要訓練一個醫學實體識別模型。你可以從本課描述的 LSTM 模型訓練開始,然後使用 BERT 轉換器模型。閱讀 [指導說明](lab/README.md) 以獲取所有細節。
|
||||
|
||||
---
|
||||
|
||||
|
|
@ -0,0 +1,50 @@
|
|||
# 命名實體識別 (NER)
|
||||
|
||||
來自 [AI for Beginners Curriculum](https://github.com/microsoft/ai-for-beginners) 的實驗課題。
|
||||
|
||||
## 任務
|
||||
|
||||
在這次實驗中,你需要訓練一個針對醫學術語的命名實體識別模型。
|
||||
|
||||
## 數據集
|
||||
|
||||
為了訓練 NER 模型,我們需要一個包含醫學實體標註的數據集。[BC5CDR 數據集](https://biocreative.bioinformatics.udel.edu/tasks/biocreative-v/track-3-cdr/) 包含來自超過 1500 篇論文的疾病和化學物質實體標註。你可以在其網站註冊後下載該數據集。
|
||||
|
||||
BC5CDR 數據集的格式如下:
|
||||
|
||||
```
|
||||
6794356|t|Tricuspid valve regurgitation and lithium carbonate toxicity in a newborn infant.
|
||||
6794356|a|A newborn with massive tricuspid regurgitation, atrial flutter, congestive heart failure, and a high serum lithium level is described. This is the first patient to initially manifest tricuspid regurgitation and atrial flutter, and the 11th described patient with cardiac disease among infants exposed to lithium compounds in the first trimester of pregnancy. Sixty-three percent of these infants had tricuspid valve involvement. Lithium carbonate may be a factor in the increasing incidence of congenital heart disease when taken during early pregnancy. It also causes neurologic depression, cyanosis, and cardiac arrhythmia when consumed prior to delivery.
|
||||
6794356 0 29 Tricuspid valve regurgitation Disease D014262
|
||||
6794356 34 51 lithium carbonate Chemical D016651
|
||||
6794356 52 60 toxicity Disease D064420
|
||||
...
|
||||
```
|
||||
|
||||
在這個數據集中,第一行和第二行分別是論文的標題和摘要,接著是各個實體的起始和結束位置,這些位置是基於標題+摘要的文本塊。此外,還提供了該實體在某些醫學本體中的本體 ID。
|
||||
|
||||
你需要編寫一些 Python 代碼,將這些數據轉換為 BIO 編碼格式。
|
||||
|
||||
## 網絡架構
|
||||
|
||||
第一次嘗試 NER 可以使用 LSTM 網絡,就像你在課程中看到的示例一樣。然而,在自然語言處理任務中,[Transformer 架構](https://en.wikipedia.org/wiki/Transformer_(machine_learning_model)),尤其是 [BERT 語言模型](https://en.wikipedia.org/wiki/BERT_(language_model)),通常能夠取得更好的效果。預訓練的 BERT 模型能夠理解語言的基本結構,並且可以通過相對較小的數據集和計算成本進行微調以完成特定任務。
|
||||
|
||||
由於我們計劃將 NER 應用於醫學場景,因此使用基於醫學文本訓練的 BERT 模型是合理的。Microsoft Research 發布了一個名為 [PubMedBERT][PubMedBERT] 的預訓練模型 ([相關論文][PubMedBERT-Pub]),該模型使用 [PubMed](https://pubmed.ncbi.nlm.nih.gov/) 資料庫中的文本進行微調。
|
||||
|
||||
訓練 Transformer 模型的標準工具是 [Hugging Face Transformers](https://huggingface.co/) 庫。該庫還包含一個由社群維護的預訓練模型倉庫,其中包括 PubMedBERT。要加載並使用這個模型,我們只需要幾行代碼:
|
||||
|
||||
```python
|
||||
model_name = "microsoft/BiomedNLP-PubMedBERT-base-uncased-abstract"
|
||||
classes = ... # number of classes: 2*entities+1
|
||||
tokenizer = AutoTokenizer.from_pretrained(model_name)
|
||||
model = BertForTokenClassification.from_pretrained(model_name, classes)
|
||||
```
|
||||
|
||||
這段代碼提供了用於標記分類任務的 `model`(基於 `classes` 的類別數),以及可以將輸入文本分割為標記的 `tokenizer` 對象。你需要將數據集轉換為 BIO 格式,並考慮 PubMedBERT 的分詞方式。你可以參考 [這段 Python 代碼](https://gist.github.com/shwars/580b55684be3328eb39ecf01b9cbbd88) 作為靈感。
|
||||
|
||||
## 收穫
|
||||
|
||||
這項任務非常接近於實際工作中可能遇到的任務,尤其是當你希望從大量自然語言文本中獲得更多洞察時。在我們的案例中,我們可以將訓練好的模型應用於 [COVID 相關論文數據集](https://www.kaggle.com/allen-institute-for-ai/CORD-19-research-challenge),並觀察能夠獲得哪些洞察。[這篇博客文章](https://soshnikov.com/science/analyzing-medical-papers-with-azure-and-text-analytics-for-health/) 和 [這篇論文](https://www.mdpi.com/2504-2289/6/1/4) 描述了使用 NER 對這些論文集進行研究的可能性。
|
||||
|
||||
**免責聲明**:
|
||||
本文件已使用人工智能翻譯服務 [Co-op Translator](https://github.com/Azure/co-op-translator) 進行翻譯。我們致力於提供準確的翻譯,但請注意,自動翻譯可能包含錯誤或不準確之處。應以原文文件作為權威來源。如涉及關鍵資訊,建議尋求專業人工翻譯。我們對因使用此翻譯而引起的任何誤解或誤釋概不負責。
|
||||
|
|
@ -0,0 +1,325 @@
|
|||
{
|
||||
"cells": [
|
||||
{
|
||||
"attachments": {},
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## 嘗試使用 OpenAI GPT\n",
|
||||
"\n",
|
||||
"此筆記本是 [AI 初學者課程](http://aka.ms/ai-beginners) 的一部分。\n",
|
||||
"\n",
|
||||
"在這個筆記本中,我們將探索如何使用 Hugging Face 的 `transformers` 庫來操作 OpenAI-GPT 模型。\n",
|
||||
"\n",
|
||||
"事不宜遲,讓我們建立文字生成管道並開始生成吧!\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 1,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stderr",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"c:\\Users\\bethanycheum\\Desktop\\AI-For-Beginners\\.venv\\lib\\site-packages\\tqdm\\auto.py:21: TqdmWarning: IProgress not found. Please update jupyter and ipywidgets. See https://ipywidgets.readthedocs.io/en/stable/user_install.html\n",
|
||||
" from .autonotebook import tqdm as notebook_tqdm\n",
|
||||
"Downloading model.safetensors: 100%|██████████| 479M/479M [04:28<00:00, 1.78MB/s] \n",
|
||||
"c:\\Users\\bethanycheum\\Desktop\\AI-For-Beginners\\.venv\\lib\\site-packages\\huggingface_hub\\file_download.py:133: UserWarning: `huggingface_hub` cache-system uses symlinks by default to efficiently store duplicated files but your machine does not support them in C:\\Users\\bethanycheum\\.cache\\huggingface\\hub. Caching files will still work but in a degraded version that might require more space on your disk. This warning can be disabled by setting the `HF_HUB_DISABLE_SYMLINKS_WARNING` environment variable. For more details, see https://huggingface.co/docs/huggingface_hub/how-to-cache#limitations.\n",
|
||||
"To support symlinks on Windows, you either need to activate Developer Mode or to run Python as an administrator. In order to see activate developer mode, see this article: https://docs.microsoft.com/en-us/windows/apps/get-started/enable-your-device-for-development\n",
|
||||
" warnings.warn(message)\n",
|
||||
"Some weights of OpenAIGPTLMHeadModel were not initialized from the model checkpoint at openai-gpt and are newly initialized: ['position_ids']\n",
|
||||
"You should probably TRAIN this model on a down-stream task to be able to use it for predictions and inference.\n",
|
||||
"Downloading (…)neration_config.json: 100%|██████████| 74.0/74.0 [00:00<00:00, 48.8kB/s]\n",
|
||||
"Downloading (…)olve/main/vocab.json: 100%|██████████| 816k/816k [00:00<00:00, 1.76MB/s]\n",
|
||||
"Downloading (…)olve/main/merges.txt: 100%|██████████| 458k/458k [00:00<00:00, 1.11MB/s]\n",
|
||||
"Downloading (…)/main/tokenizer.json: 100%|██████████| 1.27M/1.27M [00:00<00:00, 2.12MB/s]\n",
|
||||
"Xformers is not installed correctly. If you want to use memory_efficient_attention to accelerate training use the following command to install Xformers\n",
|
||||
"pip install xformers.\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"[{'generated_text': \"Hello! I am a neural network, and I want to say that i apologize for not coming to you yourself, for not helping you, and that i was too busy getting dressed and studying for a midterm. you know, the kind where the teachers are like that and they come in pairs with their boyfriends, but not with theirs. it's true, that i have had a girlfriend, and i'm only going on wednesdays and thursdays because i was too busy with college, but maybe\"},\n",
|
||||
" {'generated_text': 'Hello! I am a neural network, and I want to say that we have been blessed with a wonderful gift ; no one of us has died at all. and our spirits are strong, very strong. in one very lucky moment of luck for you, all has been given direction and destiny, and for us there are no more mysteries. the earth has been chosen for you, and that earth is now ours, and you must be forever in our hearts. \" \\n the words, as one,'},\n",
|
||||
" {'generated_text': 'Hello! I am a neural network, and I want to say that if you would just turn and face the general, you would have a nice day. \" \\n \" sure thing, \" said one of the soldiers, and started to run. the rest of the soldiers followed, shouting. the general turned to general zulu, raising his arm. the general said something in his native language, and the general immediately started to run. zulu started to move toward the wall, with the'},\n",
|
||||
" {'generated_text': 'Hello! I am a neural network, and I want to say that i am not a doctor but an anthropologist to you, a specialist, a specialist in the field of astrobiological biology, and that i am very much involved in this investigation. i am not sure, i am not certain, but i can confirm your conclusions and therefore i will go to the top. i have a colleague who has just returned from this expedition and his findings confirm that you are a specialist. that is, he'},\n",
|
||||
" {'generated_text': \"Hello! I am a neural network, and I want to say that everyone here is in agreement that no matter how many times i say to myself,'he was never a man of action on the battlefield,'or'he 'll never take a chance at killing any civilians,'or'he 'll never let his men go undefended against enemy forces of this caliber,'or'that's just what i need in a day like today. \\n you see, there are only three groups that\"}]"
|
||||
]
|
||||
},
|
||||
"execution_count": 1,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"from transformers import pipeline\n",
|
||||
"\n",
|
||||
"model_name = 'openai-gpt' \n",
|
||||
"\n",
|
||||
"generator = pipeline('text-generation', model=model_name)\n",
|
||||
"\n",
|
||||
"generator(\"Hello! I am a neural network, and I want to say that\", max_length=100, num_return_sequences=5)\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"attachments": {},
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## 提示工程\n",
|
||||
"\n",
|
||||
"在某些問題中,你可以通過設計正確的提示直接使用 openai-gpt 生成。以下是一些示例:\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 2,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"[{'generated_text': 'Synonyms of a word cat: the same cat i used to stare at, and you in'},\n",
|
||||
" {'generated_text': 'Synonyms of a word cat: cat of the woods, cat of the hills, cat of'},\n",
|
||||
" {'generated_text': 'Synonyms of a word cat: you! \\n \" it\\'s a girl. \" i said'},\n",
|
||||
" {'generated_text': \"Synonyms of a word cat: big cat. but how come, we didn't hear it\"},\n",
|
||||
" {'generated_text': 'Synonyms of a word cat: \" mea - o - c \" which makes them sound'}]"
|
||||
]
|
||||
},
|
||||
"execution_count": 2,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"generator(\"Synonyms of a word cat:\", max_length=20, num_return_sequences=5)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 3,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"[{'generated_text': 'I love when you say this -> Positive\\nI have myself -> Negative\\nThis is awful for you to say this -> positive this is so horrible - > positive that your brother is gay - >'},\n",
|
||||
" {'generated_text': 'I love when you say this -> Positive\\nI have myself -> Negative\\nThis is awful for you to say this -> negative i will bring this on you -, < positive am i, i'},\n",
|
||||
" {'generated_text': 'I love when you say this -> Positive\\nI have myself -> Negative\\nThis is awful for you to say this -> negative i have self - esteem i must take it - : \\n - -'},\n",
|
||||
" {'generated_text': 'I love when you say this -> Positive\\nI have myself -> Negative\\nThis is awful for you to say this -> negative this is - : \\n if it were true that the devil would have'},\n",
|
||||
" {'generated_text': \"I love when you say this -> Positive\\nI have myself -> Negative\\nThis is awful for you to say this -> positive i have you - > positive it's a bad thing, > positive\"}]"
|
||||
]
|
||||
},
|
||||
"execution_count": 3,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"generator(\"I love when you say this -> Positive\\nI have myself -> Negative\\nThis is awful for you to say this ->\", max_length=40, num_return_sequences=5)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 4,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"[{'generated_text': 'Translate English to French: cat => chat, dog => chien, student => new and unusual. there were no more words to be'},\n",
|
||||
" {'generated_text': 'Translate English to French: cat => chat, dog => chien, student => student \\n his eyes were huge in his lean face as'},\n",
|
||||
" {'generated_text': \"Translate English to French: cat => chat, dog => chien, student => the teacher's words, their words, their words.\"}]"
|
||||
]
|
||||
},
|
||||
"execution_count": 4,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"generator(\"Translate English to French: cat => chat, dog => chien, student => \", top_k=50, max_length=30, num_return_sequences=3)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 5,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"[{'generated_text': 'People who liked the movie The Matrix also liked it, and there was the movie of the first man after us. \\n i wanted to laugh at how stupid these stupid actors were. no, they were'},\n",
|
||||
" {'generated_text': \"People who liked the movie The Matrix also liked the movie, and the film was the result. and that's when the man in the story was brought into reality, after a few decades. \\n a\"},\n",
|
||||
" {'generated_text': 'People who liked the movie The Matrix also liked the movie the matrix, because there was a very old movie movie called the matrix, where there was a great super hero, and the super hero came out'},\n",
|
||||
" {'generated_text': \"People who liked the movie The Matrix also liked the movie that didn't have a chance to pay cash, if they could afford it. most often they got a good deal and a lot of money,\"},\n",
|
||||
" {'generated_text': \"People who liked the movie The Matrix also liked the movie, and i didn't seem to have the same problem. \\n i 'd met the other half of my family. i spent most of my time\"}]"
|
||||
]
|
||||
},
|
||||
"execution_count": 5,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"generator(\"People who liked the movie The Matrix also liked \", max_length=40, num_return_sequences=5)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## 文本取樣策略\n",
|
||||
"\n",
|
||||
"到目前為止,我們一直使用簡單的 **貪婪** 取樣策略,根據最高概率選擇下一個詞語。以下是其運作方式:\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 6,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"[{'generated_text': 'It was early evening when I can back from work. I usually work late, but this time it was an exception. When I entered a room, I saw my friend, a young man, sprawled across the bed in his bed. \\n \" hi, i\\'m mike eptirard. \" \\n there was silence on the other side of the door. i listened for any trace of life but there was nothing. my heart began to pound, i was starting to sweat, i took out my wallet'},\n",
|
||||
" {'generated_text': 'It was early evening when I can back from work. I usually work late, but this time it was an exception. When I entered a room, I saw my mother on the bed, hugging her legs to her chest and sobbing. i saw my dad and mother from the corner of my eye. \\n elfin face was covered in tears as i entered the room. my dad and mother also wept ; just as they did every other time i came to work. but this time, they had different faces'},\n",
|
||||
" {'generated_text': 'It was early evening when I can back from work. I usually work late, but this time it was an exception. When I entered a room, I saw the room had changed because it was dark. it still smelled like a hospital. a new light shined through from a vent in the ceiling. i found myself in a bathroom and a small room with a sink and a wall of glass. the bathroom billion years ago. not so different from all of the rest of the apartment. \\n now...'},\n",
|
||||
" {'generated_text': 'It was early evening when I can back from work. I usually work late, but this time it was an exception. When I entered a room, I saw a large woman with dark hair and pale skin. she was asleep, but i noticed a faint movement of her face. i could sense she was awake. i got up and walked over to her. \\n \" hello miss. i am inspector michael o\\'dell ; we are investigating the case against you. i wanted to ask if you were the'},\n",
|
||||
" {'generated_text': 'It was early evening when I can back from work. I usually work late, but this time it was an exception. When I entered a room, I saw i had an empty table and three empty chairs. that was all i needed. i had left a note on a table in the center of the room and had a pen in hand. \" \\n \" i think what you were doing was something he was doing to her. \" \\n \" yeah, \" i nodded with a grin. \" i'}]"
|
||||
]
|
||||
},
|
||||
"execution_count": 6,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"prompt = \"It was early evening when I can back from work. I usually work late, but this time it was an exception. When I entered a room, I saw\"\n",
|
||||
"generator(prompt,max_length=100,num_return_sequences=5)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"**束搜索**允許生成器探索多個文本生成的方向(*束*),並選擇整體分數較高的方向。您可以通過提供 `num_beams` 參數來進行束搜索。您還可以指定 `no_repeat_ngram_size`,以懲罰模型重複給定大小的 n-gram:\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 7,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"[{'generated_text': 'It was early evening when I can back from work. I usually work late, but this time it was an exception. When I entered a room, I saw a man sitting in a chair with his head in his hands. he didn\\'t look up as i approached. \\n \" excuse me, sir, \" i said. \" can i help you? \" \\n the man looked up at me. his eyes were red - rimmed and his face was pale, as if he hadn\\'t slept in days'},\n",
|
||||
" {'generated_text': 'It was early evening when I can back from work. I usually work late, but this time it was an exception. When I entered a room, I saw a man sitting at a desk in the middle of the room. he had his back to me, so i couldn\\'t see what he was doing. \" \\n \" what did he look like? \" i asked as i sat down on the bed next to her. \\n she took a deep breath and looked at me with tears in her eyes'},\n",
|
||||
" {'generated_text': 'It was early evening when I can back from work. I usually work late, but this time it was an exception. When I entered a room, I saw a woman sitting on the bed, reading a book. she looked up at me and smiled. \\n \" hi, \" she said. \" can i help you? \" \\n i sat down next to her and looked around the room. the walls were white, and there was a large window in the middle of the wall that looked out on'},\n",
|
||||
" {'generated_text': 'It was early evening when I can back from work. I usually work late, but this time it was an exception. When I entered a room, I saw a man sitting at a table in the middle of the room. he looked up as i walked in, and when he saw me, he got up and walked over to me. \\n \" can i help you? \" he asked as he put his hand on the small of my back and led me to a chair at the other end of'},\n",
|
||||
" {'generated_text': 'It was early evening when I can back from work. I usually work late, but this time it was an exception. When I entered a room, I saw a woman sitting on the edge of her bed, reading a book. she looked up at me and smiled. \\n \" hello, \" she said. \" can i help you? \" \\n i didn\\'t know what to say, so i just sat down in the chair next to the bed and looked at her. her hair was dark brown'}]"
|
||||
]
|
||||
},
|
||||
"execution_count": 7,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"prompt = \"It was early evening when I can back from work. I usually work late, but this time it was an exception. When I entered a room, I saw\"\n",
|
||||
"generator(prompt,max_length=100,num_return_sequences=5,num_beams=10,no_repeat_ngram_size=2)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"**抽樣** 根據模型返回的概率分佈,以非確定性的方式選擇下一個詞語。您可以使用參數 `do_sample=True` 開啟抽樣功能。您還可以指定 `temperature`,以調整模型的確定性程度。\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 8,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"[{'generated_text': 'It was early evening when I can back from work. I usually work late, but this time it was an exception. When I entered a room, I saw her. she was on the bed, but she looked very different. \\n \" honey, what\\'s the matter? \" i asked. \\n she sat up. \" i can\\'t believe it\\'s real. i\\'ve been dreaming about you for the last two days. \" \\n \" i can\\'t believe it either. i guess that\\'s how'}]"
|
||||
]
|
||||
},
|
||||
"execution_count": 8,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"prompt = \"It was early evening when I can back from work. I usually work late, but this time it was an exception. When I entered a room, I saw\"\n",
|
||||
"generator(prompt,max_length=100,do_sample=True,temperature=0.8)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"我們還可以為採樣提供額外的參數: \n",
|
||||
"* `top_k` 指定在使用採樣時需要考慮的詞彙選項數量。這可以減少在文本中出現奇怪(低概率)詞彙的機會。 \n",
|
||||
"* `top_p` 類似,但我們選擇的是總概率大於 p 的最可能詞彙的最小子集。 \n",
|
||||
"\n",
|
||||
"可以隨意嘗試加入這些參數進行實驗。 \n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"attachments": {},
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## 微調您的模型\n",
|
||||
"\n",
|
||||
"您亦可使用自己的數據集[微調您的模型](https://learn.microsoft.com/en-us/azure/cognitive-services/openai/how-to/fine-tuning?pivots=programming-language-studio?WT.mc_id=academic-77998-bethanycheum)。這樣可以在保留語言模型主要部分的同時,調整文本的風格。\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"\n---\n\n**免責聲明**: \n本文件已使用人工智能翻譯服務 [Co-op Translator](https://github.com/Azure/co-op-translator) 進行翻譯。我們致力於提供準確的翻譯,但請注意,自動翻譯可能包含錯誤或不準確之處。應以原文文件作為權威來源。對於關鍵資訊,建議尋求專業人工翻譯。我們對因使用此翻譯而引起的任何誤解或誤釋不承擔責任。\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"interpreter": {
|
||||
"hash": "16af2a8bbb083ea23e5e41c7f5787656b2ce26968575d8763f2c4b17f9cd711f"
|
||||
},
|
||||
"kernelspec": {
|
||||
"display_name": "Python 3.8.12 ('py38')",
|
||||
"language": "python",
|
||||
"name": "python3"
|
||||
},
|
||||
"language_info": {
|
||||
"codemirror_mode": {
|
||||
"name": "ipython",
|
||||
"version": 3
|
||||
},
|
||||
"file_extension": ".py",
|
||||
"mimetype": "text/x-python",
|
||||
"name": "python",
|
||||
"nbconvert_exporter": "python",
|
||||
"pygments_lexer": "ipython3",
|
||||
"version": "3.10.11"
|
||||
},
|
||||
"orig_nbformat": 4,
|
||||
"coopTranslator": {
|
||||
"original_hash": "d4ff89615d38924a55594f16d6d20678",
|
||||
"translation_date": "2025-08-31T10:44:25+00:00",
|
||||
"source_file": "lessons/5-NLP/20-LangModels/GPT-PyTorch.ipynb",
|
||||
"language_code": "hk"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 2
|
||||
}
|
||||
|
|
@ -0,0 +1,55 @@
|
|||
# 預訓練大型語言模型
|
||||
|
||||
在之前的所有任務中,我們都使用標註數據集來訓練神經網絡以完成特定任務。而對於像 BERT 這樣的大型 Transformer 模型,我們使用自監督的方式進行語言建模來構建語言模型,然後通過進一步的特定領域訓練使其專門化於特定的下游任務。然而,研究表明,大型語言模型也能在沒有任何特定領域訓練的情況下解決許多任務。能夠做到這一點的一類模型被稱為 **GPT**:生成式預訓練 Transformer。
|
||||
|
||||
## [課前測驗](https://ff-quizzes.netlify.app/en/ai/quiz/39)
|
||||
|
||||
## 文本生成與困惑度
|
||||
|
||||
神經網絡能夠在沒有下游訓練的情況下完成一般任務的概念在 [Language Models are Unsupervised Multitask Learners](https://cdn.openai.com/better-language-models/language_models_are_unsupervised_multitask_learners.pdf) 論文中提出。其核心思想是許多其他任務可以通過 **文本生成** 來建模,因為理解文本本質上意味著能夠生成文本。由於模型是在包含人類知識的大量文本上進行訓練,它也因此對各種主題有一定的了解。
|
||||
|
||||
> 理解並能夠生成文本也意味著對周圍世界有一定的認知。人類在很大程度上也是通過閱讀來學習,而 GPT 網絡在這方面與人類相似。
|
||||
|
||||
文本生成網絡通過預測下一個詞的概率 $$P(w_N)$$ 來工作。然而,下一個詞的無條件概率等於該詞在文本語料庫中的出現頻率。GPT 能夠給出基於前幾個詞的 **條件概率**:$$P(w_N | w_{n-1}, ..., w_0)$$
|
||||
|
||||
> 您可以在我們的 [Data Science for Beginners Curriculum](https://github.com/microsoft/Data-Science-For-Beginners/tree/main/1-Introduction/04-stats-and-probability) 中了解更多關於概率的內容。
|
||||
|
||||
語言生成模型的質量可以通過 **困惑度** 來定義。這是一種內在的度量方法,允許我們在沒有任何特定任務數據集的情況下衡量模型的質量。它基於 *句子的概率* 的概念——模型對可能是真實的句子賦予高概率(即模型對其不 **困惑**),而對那些不太合理的句子(例如 *Can it does what?*)賦予低概率。當我們給模型提供來自真實文本語料庫的句子時,我們期望它們具有高概率和低 **困惑度**。數學上,困惑度被定義為測試集的歸一化逆概率:
|
||||
$$
|
||||
\mathrm{Perplexity}(W) = \sqrt[N]{1\over P(W_1,...,W_N)}
|
||||
$$
|
||||
|
||||
**您可以使用 [Hugging Face 的 GPT 驅動文本編輯器](https://transformer.huggingface.co/doc/gpt2-large) 來試驗文本生成**。在這個編輯器中,您可以開始撰寫文本,按下 **[TAB]** 鍵後會提供幾個完成選項。如果選項太短或您不滿意,可以再次按下 [TAB] 鍵,您將獲得更多選項,包括更長的文本片段。
|
||||
|
||||
## GPT 是一個家族
|
||||
|
||||
GPT 不是單一模型,而是一系列由 [OpenAI](https://openai.com) 開發和訓練的模型。
|
||||
|
||||
在 GPT 模型家族中,我們有:
|
||||
|
||||
| [GPT-2](https://huggingface.co/docs/transformers/model_doc/gpt2#openai-gpt2) | [GPT-3](https://openai.com/research/language-models-are-few-shot-learners) | [GPT-4](https://openai.com/gpt-4) |
|
||||
| -- | -- | -- |
|
||||
|擁有最多 15 億參數的語言模型。| 擁有最多 175 億參數的語言模型 | 擁有 100 萬億參數,並且接受圖像和文本輸入,輸出文本。 |
|
||||
|
||||
GPT-3 和 GPT-4 模型可通過 [Microsoft Azure 的認知服務](https://azure.microsoft.com/en-us/services/cognitive-services/openai-service/#overview?WT.mc_id=academic-77998-cacaste) 和 [OpenAI API](https://openai.com/api/) 使用。
|
||||
|
||||
## 提示工程
|
||||
|
||||
由於 GPT 已經在大量數據上進行訓練以理解語言和代碼,它能根據輸入(提示)生成輸出。提示是 GPT 的輸入或查詢,通過提示提供指令讓模型完成下一步任務。為了獲得理想的結果,您需要最有效的提示,這涉及選擇合適的詞語、格式、短語甚至符號。這種方法被稱為 [提示工程](https://learn.microsoft.com/en-us/shows/ai-show/the-basics-of-prompt-engineering-with-azure-openai-service?WT.mc_id=academic-77998-bethanycheum)。
|
||||
|
||||
[此文檔](https://learn.microsoft.com/en-us/semantic-kernel/prompt-engineering/?WT.mc_id=academic-77998-bethanycheum) 提供了更多關於提示工程的信息。
|
||||
|
||||
## ✍️ 示例筆記本: [Playing with OpenAI-GPT](GPT-PyTorch.ipynb)
|
||||
|
||||
繼續學習以下筆記本:
|
||||
|
||||
* [使用 OpenAI-GPT 和 Hugging Face Transformers 生成文本](GPT-PyTorch.ipynb)
|
||||
|
||||
## 結論
|
||||
|
||||
新的通用預訓練語言模型不僅能建模語言結構,還包含大量自然語言。因此,它們可以在零樣本或少樣本設置中有效地解決一些 NLP 任務。
|
||||
|
||||
## [課後測驗](https://ff-quizzes.netlify.app/en/ai/quiz/40)
|
||||
|
||||
---
|
||||
|
||||
|
|
@ -0,0 +1,69 @@
|
|||
# 自然語言處理
|
||||
|
||||

|
||||
|
||||
在本節中,我們將專注於使用神經網絡來處理與**自然語言處理 (NLP)** 相關的任務。我們希望計算機能夠解決許多 NLP 問題:
|
||||
|
||||
* **文本分類** 是一種典型的文本序列分類問題。例如,將電子郵件分類為垃圾郵件或非垃圾郵件,或者將文章分類為體育、商業、政治等。此外,在開發聊天機器人時,我們經常需要理解用戶的意圖——這種情況下我們處理的是**意圖分類**。意圖分類通常需要處理許多類別。
|
||||
* **情感分析** 是一種典型的回歸問題,我們需要為句子的正面/負面情感賦予一個數值。一種更高級的情感分析是**基於方面的情感分析** (ABSA),我們不是為整個句子賦予情感,而是為句子的不同部分(方面)賦予情感,例如:*在這家餐廳,我喜歡菜餚,但氣氛很糟糕*。
|
||||
* **命名實體識別** (NER) 是從文本中提取特定實體的問題。例如,我們需要理解在短語 *我明天需要飛往巴黎* 中,*明天* 指的是日期 (DATE),而 *巴黎* 是地點 (LOCATION)。
|
||||
* **關鍵詞提取** 與 NER 類似,但我們需要自動提取對句子意義重要的詞語,而不需要針對特定實體類型進行預訓練。
|
||||
* **文本聚類** 在我們希望將相似的句子分組時非常有用,例如,技術支持對話中相似的請求。
|
||||
* **問答系統** 是指模型回答特定問題的能力。模型接收一段文本和一個問題作為輸入,並需要提供文本中包含答案的位置(有時需要生成答案文本)。
|
||||
* **文本生成** 是模型生成新文本的能力。這可以被視為一種分類任務,根據某些*文本提示*預測下一個字母/單詞。高級文本生成模型(如 GPT-3)能夠通過一種稱為[提示編程](https://towardsdatascience.com/software-3-0-how-prompting-will-change-the-rules-of-the-game-a982fbfe1e0)或[提示工程](https://medium.com/swlh/openai-gpt-3-and-prompt-engineering-dcdc2c5fcd29)的技術解決其他 NLP 任務,例如分類。
|
||||
* **文本摘要** 是一種技術,我們希望計算機能夠“閱讀”長文本並將其總結為幾句話。
|
||||
* **機器翻譯** 可以被視為一種語言理解和另一種語言文本生成的結合。
|
||||
|
||||
最初,大多數 NLP 任務是通過傳統方法(如語法)解決的。例如,在機器翻譯中,解析器被用來將初始句子轉換為語法樹,然後提取高層語義結構來表示句子的含義,基於這些含義和目標語言的語法生成結果。如今,許多 NLP 任務使用神經網絡更有效地解決。
|
||||
|
||||
> 許多經典的 NLP 方法已在 [Natural Language Processing Toolkit (NLTK)](https://www.nltk.org) Python 庫中實現。網上有一本很棒的 [NLTK 書](https://www.nltk.org/book/),涵蓋了如何使用 NLTK 解決不同的 NLP 任務。
|
||||
|
||||
在我們的課程中,我們將主要專注於使用神經網絡進行 NLP,並在需要時使用 NLTK。
|
||||
|
||||
我們已經學習了如何使用神經網絡處理表格數據和圖像。這些數據類型與文本的主要區別在於,文本是可變長度的序列,而圖像的輸入大小是事先已知的。雖然卷積網絡可以從輸入數據中提取模式,但文本中的模式更為複雜。例如,否定可能與主語分隔許多單詞(例如:*我不喜歡橙子*,與 *我不喜歡那些又大又多彩又美味的橙子*),但仍應被解釋為一個模式。因此,為了處理語言,我們需要引入新的神經網絡類型,例如*循環網絡*和*變壓器*。
|
||||
|
||||
## 安裝庫
|
||||
|
||||
如果您使用本地 Python 安裝來運行本課程,您可能需要使用以下命令安裝 NLP 所需的所有庫:
|
||||
|
||||
**對於 PyTorch**
|
||||
```bash
|
||||
pip install -r requirements-torch.txt
|
||||
```
|
||||
**對於 TensorFlow**
|
||||
```bash
|
||||
pip install -r requirements-tf.txt
|
||||
```
|
||||
|
||||
> 您可以在 [Microsoft Learn](https://docs.microsoft.com/learn/modules/intro-natural-language-processing-tensorflow/?WT.mc_id=academic-77998-cacaste) 上嘗試使用 TensorFlow 進行 NLP。
|
||||
|
||||
## GPU 警告
|
||||
|
||||
在本節中,我們的一些示例將訓練相當大的模型。
|
||||
* **使用支持 GPU 的電腦**:建議在支持 GPU 的電腦上運行筆記本,以減少處理大型模型時的等待時間。
|
||||
* **GPU 記憶體限制**:在 GPU 上運行可能會導致 GPU 記憶體不足的情況,特別是在訓練大型模型時。
|
||||
* **GPU 記憶體消耗**:訓練過程中消耗的 GPU 記憶體量取決於多種因素,包括小批量大小。
|
||||
* **減小小批量大小**:如果遇到 GPU 記憶體問題,可以考慮減小代碼中的小批量大小作為潛在解決方案。
|
||||
* **TensorFlow GPU 記憶體釋放**:舊版本的 TensorFlow 在一個 Python 內核中訓練多個模型時可能無法正確釋放 GPU 記憶體。為了有效管理 GPU 記憶體使用,您可以配置 TensorFlow 僅在需要時分配 GPU 記憶體。
|
||||
* **代碼包含**:要設置 TensorFlow 僅在需要時增長 GPU 記憶體分配,請在筆記本中包含以下代碼:
|
||||
|
||||
```python
|
||||
physical_devices = tf.config.list_physical_devices('GPU')
|
||||
if len(physical_devices)>0:
|
||||
tf.config.experimental.set_memory_growth(physical_devices[0], True)
|
||||
```
|
||||
|
||||
如果您對從經典機器學習角度學習 NLP 感興趣,請訪問[這套課程](https://github.com/microsoft/ML-For-Beginners/tree/main/6-NLP)。
|
||||
|
||||
## 本節內容
|
||||
在本節中,我們將學習:
|
||||
|
||||
* [將文本表示為張量](13-TextRep/README.md)
|
||||
* [詞嵌入](14-Emdeddings/README.md)
|
||||
* [語言建模](15-LanguageModeling/README.md)
|
||||
* [循環神經網絡](16-RNN/README.md)
|
||||
* [生成網絡](17-GenerativeNetworks/README.md)
|
||||
* [變壓器](18-Transformers/README.md)
|
||||
|
||||
**免責聲明**:
|
||||
本文件已使用人工智能翻譯服務 [Co-op Translator](https://github.com/Azure/co-op-translator) 進行翻譯。我們致力於提供準確的翻譯,但請注意,自動翻譯可能包含錯誤或不準確之處。應以原文文件作為權威來源。對於關鍵資訊,建議尋求專業人工翻譯。我們對因使用此翻譯而引起的任何誤解或錯誤解釋概不負責。
|
||||
|
|
@ -0,0 +1,49 @@
|
|||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## 作業:丟番圖方程\n",
|
||||
"\n",
|
||||
"> 此作業是 [AI for Beginners Curriculum](http://github.com/microsoft/ai-for-beginners) 的一部分,靈感來自 [這篇文章](https://habr.com/post/128704/)。\n",
|
||||
"\n",
|
||||
"你的目標是解決所謂的 **丟番圖方程**——一種具有整數根和整數係數的方程。例如,考慮以下方程:\n",
|
||||
"\n",
|
||||
"$$a+2b+3c+4d=30$$\n",
|
||||
"\n",
|
||||
"你需要找到整數根 $a$,$b$,$c$,$d\\in\\mathbb{N}$,使其滿足這個方程。\n",
|
||||
"\n",
|
||||
"提示:\n",
|
||||
"1. 你可以考慮根值在區間 [0;30] 之內\n",
|
||||
"1. 作為基因,可以考慮使用根值的列表\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": []
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"\n---\n\n**免責聲明**: \n本文件已使用人工智能翻譯服務 [Co-op Translator](https://github.com/Azure/co-op-translator) 進行翻譯。儘管我們致力於提供準確的翻譯,但請注意,自動翻譯可能包含錯誤或不準確之處。原始文件的母語版本應被視為權威來源。對於重要信息,建議使用專業人工翻譯。我們對因使用此翻譯而引起的任何誤解或錯誤解釋不承擔責任。\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"language_info": {
|
||||
"name": "python"
|
||||
},
|
||||
"orig_nbformat": 4,
|
||||
"coopTranslator": {
|
||||
"original_hash": "a967e1fa1e11ab2b6467b19349a4a9aa",
|
||||
"translation_date": "2025-08-31T09:21:19+00:00",
|
||||
"source_file": "lessons/6-Other/21-GeneticAlgorithms/Diophantine.ipynb",
|
||||
"language_code": "hk"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 2
|
||||
}
|
||||
File diff suppressed because one or more lines are too long
|
|
@ -0,0 +1,80 @@
|
|||
# 遺傳算法
|
||||
|
||||
## [課前測驗](https://ff-quizzes.netlify.app/en/ai/quiz/41)
|
||||
|
||||
**遺傳算法** (GA) 是基於一種**進化式方法**的人工智能技術,通過模仿群體進化的方式來尋求某個問題的最佳解。該算法由 [John Henry Holland](https://wikipedia.org/wiki/John_Henry_Holland) 在1975年提出。
|
||||
|
||||
遺傳算法基於以下理念:
|
||||
|
||||
* 問題的有效解可以表示為**基因**
|
||||
* **交叉**允許我們將兩個解結合起來,生成新的有效解
|
||||
* 使用某種**適應度函數**進行**選擇**,以篩選出更優的解
|
||||
* 引入**突變**以打破優化的穩定性,幫助跳出局部最小值
|
||||
|
||||
如果你想實現遺傳算法,需要以下步驟:
|
||||
|
||||
* 找到一種方法,用**基因** g∈Γ 來編碼問題的解
|
||||
* 在基因集合 Γ 上定義**適應度函數** fit: Γ→**R**,函數值越小表示解越好
|
||||
* 定義**交叉**機制,將兩個基因結合生成新的有效解 crossover: Γ<sup>2</sub>→Γ
|
||||
* 定義**突變**機制 mutate: Γ→Γ
|
||||
|
||||
在很多情況下,交叉和突變是相對簡單的算法,用於操作基因的數字序列或位向量。
|
||||
|
||||
遺傳算法的具體實現可能因情況而異,但其整體結構如下:
|
||||
|
||||
1. 選擇初始群體 G⊂Γ
|
||||
2. 隨機選擇本步驟要執行的操作:交叉或突變
|
||||
3. **交叉**:
|
||||
* 隨機選擇兩個基因 g<sub>1</sub>, g<sub>2</sub> ∈ G
|
||||
* 計算交叉 g=crossover(g<sub>1</sub>,g<sub>2</sub>)
|
||||
* 如果 fit(g)<fit(g<sub>1</sub>) 或 fit(g)<fit(g<sub>2</sub>),則用 g 替換群體中的相應基因
|
||||
4. **突變** - 隨機選擇一個基因 g∈G,並用 mutate(g) 替換它
|
||||
5. 從第2步開始重複,直到適應度函數值足夠小,或者達到步驟數限制。
|
||||
|
||||
## 常見任務
|
||||
|
||||
遺傳算法通常用於解決以下任務:
|
||||
|
||||
1. 排程優化
|
||||
1. 最佳打包
|
||||
1. 最佳切割
|
||||
1. 加速窮舉搜索
|
||||
|
||||
## ✍️ 練習:遺傳算法
|
||||
|
||||
繼續學習以下筆記本:
|
||||
|
||||
前往[這個筆記本](Genetic.ipynb),查看使用遺傳算法的兩個例子:
|
||||
|
||||
1. 公平分配寶藏
|
||||
1. 八皇后問題
|
||||
|
||||
## 結論
|
||||
|
||||
遺傳算法被用於解決許多問題,包括物流和搜索問題。這一領域的靈感來自心理學和計算機科學的交叉研究。
|
||||
|
||||
## 🚀 挑戰
|
||||
|
||||
「遺傳算法易於實現,但其行為難以理解。」[來源](https://wikipedia.org/wiki/Genetic_algorithm) 請進行一些研究,找到一個遺傳算法的實現,例如解數獨問題,並以草圖或流程圖的形式解釋其工作原理。
|
||||
|
||||
## [課後測驗](https://ff-quizzes.netlify.app/en/ai/quiz/42)
|
||||
|
||||
## 回顧與自學
|
||||
|
||||
觀看[這段精彩視頻](https://www.youtube.com/watch?v=qv6UVOQ0F44),了解計算機如何通過遺傳算法訓練的神經網絡學習玩《超級瑪利歐》。我們將在[下一節](../22-DeepRL/README.md)中學習更多有關計算機學習玩遊戲的內容。
|
||||
|
||||
## [作業:丟番圖方程](Diophantine.ipynb)
|
||||
|
||||
你的目標是解決所謂的**丟番圖方程**——一種具有整數根的方程。例如,考慮方程 a+2b+3c+4d=30。你需要找到滿足該方程的整數根。
|
||||
|
||||
*此作業的靈感來自[這篇文章](https://habr.com/post/128704/)。*
|
||||
|
||||
提示:
|
||||
|
||||
1. 你可以考慮根的範圍為 [0;30]
|
||||
1. 作為基因,可以考慮使用根值的列表
|
||||
|
||||
使用 [Diophantine.ipynb](Diophantine.ipynb) 作為起點。
|
||||
|
||||
---
|
||||
|
||||
|
|
@ -0,0 +1,501 @@
|
|||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"# 使用強化學習訓練模型平衡Cartpole\n",
|
||||
"\n",
|
||||
"這份筆記本是 [AI for Beginners Curriculum](http://aka.ms/ai-beginners) 的一部分,靈感來自 [官方 PyTorch 教程](https://pytorch.org/tutorials/intermediate/reinforcement_q_learning.html) 和 [這個 Cartpole PyTorch 實現](https://github.com/yc930401/Actor-Critic-pytorch)。\n",
|
||||
"\n",
|
||||
"在這個例子中,我們將使用強化學習(RL)訓練一個模型,使其能夠平衡一根安裝在小車上的杆。小車可以在水平軸上向左或向右移動。我們將使用 [OpenAI Gym](https://www.gymlibrary.ml/) 環境來模擬這個場景。\n",
|
||||
"\n",
|
||||
"> **注意**:你可以在本地(例如使用 Visual Studio Code)運行這節課的代碼,此時模擬器會在新窗口中打開。如果在線運行代碼,你可能需要對代碼進行一些調整,具體請參考 [這裡](https://towardsdatascience.com/rendering-openai-gym-envs-on-binder-and-google-colab-536f99391cc7)。\n",
|
||||
"\n",
|
||||
"我們將從確保 Gym 已安裝開始:\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import sys\n",
|
||||
"!{sys.executable} -m pip install gym"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"現在讓我們建立 CartPole 環境,並了解如何操作它。一個環境具有以下特性:\n",
|
||||
"\n",
|
||||
"* **動作空間** 是我們在模擬的每一步中可以執行的所有可能動作的集合 \n",
|
||||
"* **觀察空間** 是我們可以進行觀察的範圍\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import gym\n",
|
||||
"\n",
|
||||
"env = gym.make(\"CartPole-v1\")\n",
|
||||
"\n",
|
||||
"print(f\"Action space: {env.action_space}\")\n",
|
||||
"print(f\"Observation space: {env.observation_space}\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"讓我們看看模擬是如何運作的。以下的迴圈會執行模擬,直到 `env.step` 不再返回終止標誌 `done` 為止。我們會使用 `env.action_space.sample()` 隨機選擇動作,這意味著實驗可能會非常快地失敗(當小車的速度、位置或角度超出某些限制時,CartPole 環境就會終止)。\n",
|
||||
"\n",
|
||||
"> 模擬將會在新視窗中開啟。你可以多次執行程式碼,觀察它的行為。\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"env.reset()\n",
|
||||
"\n",
|
||||
"done = False\n",
|
||||
"total_reward = 0\n",
|
||||
"while not done:\n",
|
||||
" env.render()\n",
|
||||
" obs, rew, done, info = env.step(env.action_space.sample())\n",
|
||||
" total_reward += rew\n",
|
||||
" print(f\"{obs} -> {rew}\")\n",
|
||||
"print(f\"Total reward: {total_reward}\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"你可以注意到觀察值包含四個數字,它們分別是:\n",
|
||||
"- 小車的位置\n",
|
||||
"- 小車的速度\n",
|
||||
"- 杆子的角度\n",
|
||||
"- 杆子的旋轉速率\n",
|
||||
"\n",
|
||||
"`rew` 是我們在每一步獲得的獎勵。在 CartPole 環境中,每模擬一步你都會獲得 1 分,目標是最大化總獎勵,也就是讓 CartPole 能夠平衡而不倒下的時間越長越好。\n",
|
||||
"\n",
|
||||
"在強化學習中,我們的目標是訓練一個 **策略** $\\pi$,它會根據每個狀態 $s$ 告訴我們應該採取的行動 $a$,基本上就是 $a = \\pi(s)$。\n",
|
||||
"\n",
|
||||
"如果你想要一個概率性的解法,可以將策略理解為對每個行動返回一組概率,也就是 $\\pi(a|s)$ 表示在狀態 $s$ 下採取行動 $a$ 的概率。\n",
|
||||
"\n",
|
||||
"## 策略梯度方法\n",
|
||||
"\n",
|
||||
"在最簡單的強化學習算法中,稱為 **策略梯度**,我們將訓練一個神經網絡來預測下一步的行動。\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import numpy as np\n",
|
||||
"import matplotlib.pyplot as plt\n",
|
||||
"import torch\n",
|
||||
"\n",
|
||||
"num_inputs = 4\n",
|
||||
"num_actions = 2\n",
|
||||
"\n",
|
||||
"model = torch.nn.Sequential(\n",
|
||||
" torch.nn.Linear(num_inputs, 128, bias=False, dtype=torch.float32),\n",
|
||||
" torch.nn.ReLU(),\n",
|
||||
" torch.nn.Linear(128, num_actions, bias = False, dtype=torch.float32),\n",
|
||||
" torch.nn.Softmax(dim=1)\n",
|
||||
")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"我們將通過進行多次實驗來訓練網絡,並在每次運行後更新網絡。讓我們定義一個函數來運行實驗並返回結果(即所謂的**追蹤**)——所有狀態、行動(及其建議的概率)和獎勵:\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"def run_episode(max_steps_per_episode = 10000,render=False): \n",
|
||||
" states, actions, probs, rewards = [],[],[],[]\n",
|
||||
" state = env.reset()\n",
|
||||
" for _ in range(max_steps_per_episode):\n",
|
||||
" if render:\n",
|
||||
" env.render()\n",
|
||||
" action_probs = model(torch.from_numpy(np.expand_dims(state,0)))[0]\n",
|
||||
" action = np.random.choice(num_actions, p=np.squeeze(action_probs.detach().numpy()))\n",
|
||||
" nstate, reward, done, info = env.step(action)\n",
|
||||
" if done:\n",
|
||||
" break\n",
|
||||
" states.append(state)\n",
|
||||
" actions.append(action)\n",
|
||||
" probs.append(action_probs.detach().numpy())\n",
|
||||
" rewards.append(reward)\n",
|
||||
" state = nstate\n",
|
||||
" return np.vstack(states), np.vstack(actions), np.vstack(probs), np.vstack(rewards)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"您可以使用未訓練的網絡運行一個劇集,並觀察到總回報(即劇集的長度)非常低:\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"s, a, p, r = run_episode()\n",
|
||||
"print(f\"Total reward: {np.sum(r)}\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"政策梯度算法的一個棘手方面是使用**折扣回報**。其想法是我們在遊戲的每一步計算總回報的向量,並在此過程中使用某個係數 $gamma$ 對早期回報進行折扣。我們還會對結果向量進行標準化,因為我們將使用它作為權重來影響我們的訓練:\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"eps = 0.0001\n",
|
||||
"\n",
|
||||
"def discounted_rewards(rewards,gamma=0.99,normalize=True):\n",
|
||||
" ret = []\n",
|
||||
" s = 0\n",
|
||||
" for r in rewards[::-1]:\n",
|
||||
" s = r + gamma * s\n",
|
||||
" ret.insert(0, s)\n",
|
||||
" if normalize:\n",
|
||||
" ret = (ret-np.mean(ret))/(np.std(ret)+eps)\n",
|
||||
" return ret"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"現在讓我們開始實際的訓練!我們將運行 300 次實驗,每次實驗中我們將執行以下步驟:\n",
|
||||
"\n",
|
||||
"1. 運行實驗並收集追蹤數據\n",
|
||||
"2. 計算所採取行動與預測機率之間的差異(`gradients`)。差異越小,表示我們越確定採取了正確的行動。\n",
|
||||
"3. 計算折扣後的回報,並將梯度乘以折扣後的回報——這樣可以確保高回報的步驟對最終結果的影響比低回報的步驟更大。\n",
|
||||
"4. 我們的神經網絡的預期目標行動部分來自運行期間的預測機率,部分來自計算出的梯度。我們將使用 `alpha` 參數來決定梯度和回報在多大程度上被考慮——這被稱為強化學習算法的*學習率*。\n",
|
||||
"5. 最後,我們基於狀態和預期行動訓練我們的網絡,並重複這個過程。\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"optimizer = torch.optim.Adam(model.parameters(), lr=0.01)\n",
|
||||
"\n",
|
||||
"def train_on_batch(x, y):\n",
|
||||
" x = torch.from_numpy(x)\n",
|
||||
" y = torch.from_numpy(y)\n",
|
||||
" optimizer.zero_grad()\n",
|
||||
" predictions = model(x)\n",
|
||||
" loss = -torch.mean(torch.log(predictions) * y)\n",
|
||||
" loss.backward()\n",
|
||||
" optimizer.step()\n",
|
||||
" return loss"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"alpha = 1e-4\n",
|
||||
"\n",
|
||||
"history = []\n",
|
||||
"for epoch in range(300):\n",
|
||||
" states, actions, probs, rewards = run_episode()\n",
|
||||
" one_hot_actions = np.eye(2)[actions.T][0]\n",
|
||||
" gradients = one_hot_actions-probs\n",
|
||||
" dr = discounted_rewards(rewards)\n",
|
||||
" gradients *= dr\n",
|
||||
" target = alpha*np.vstack([gradients])+probs\n",
|
||||
" train_on_batch(states,target)\n",
|
||||
" history.append(np.sum(rewards))\n",
|
||||
" if epoch%100==0:\n",
|
||||
" print(f\"{epoch} -> {np.sum(rewards)}\")\n",
|
||||
"\n",
|
||||
"plt.plot(history)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"現在讓我們運行帶有渲染的劇集來查看結果:\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"_ = run_episode(render=True)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"希望你可以看到,現在杆子已經能夠相當穩定地平衡了!\n",
|
||||
"\n",
|
||||
"## Actor-Critic 模型\n",
|
||||
"\n",
|
||||
"Actor-Critic 模型是策略梯度的進一步發展,在這個模型中,我們建立了一個神經網絡來同時學習策略和估算的回報。這個網絡會有兩個輸出(或者你可以將其視為兩個獨立的網絡):\n",
|
||||
"* **Actor** 會根據策略梯度模型,通過提供狀態概率分佈來推薦應採取的行動。\n",
|
||||
"* **Critic** 則會估算這些行動可能帶來的回報。它會在給定的狀態下返回未來的總估算回報。\n",
|
||||
"\n",
|
||||
"讓我們定義這樣一個模型:\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from itertools import count\n",
|
||||
"import torch.nn.functional as F"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"device = torch.device(\"cuda\" if torch.cuda.is_available() else \"cpu\")\n",
|
||||
"env = gym.make(\"CartPole-v1\")\n",
|
||||
"\n",
|
||||
"state_size = env.observation_space.shape[0]\n",
|
||||
"action_size = env.action_space.n\n",
|
||||
"lr = 0.0001\n",
|
||||
"\n",
|
||||
"class Actor(torch.nn.Module):\n",
|
||||
" def __init__(self, state_size, action_size):\n",
|
||||
" super(Actor, self).__init__()\n",
|
||||
" self.state_size = state_size\n",
|
||||
" self.action_size = action_size\n",
|
||||
" self.linear1 = torch.nn.Linear(self.state_size, 128)\n",
|
||||
" self.linear2 = torch.nn.Linear(128, 256)\n",
|
||||
" self.linear3 = torch.nn.Linear(256, self.action_size)\n",
|
||||
"\n",
|
||||
" def forward(self, state):\n",
|
||||
" output = F.relu(self.linear1(state))\n",
|
||||
" output = F.relu(self.linear2(output))\n",
|
||||
" output = self.linear3(output)\n",
|
||||
" distribution = torch.distributions.Categorical(F.softmax(output, dim=-1))\n",
|
||||
" return distribution\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"class Critic(torch.nn.Module):\n",
|
||||
" def __init__(self, state_size, action_size):\n",
|
||||
" super(Critic, self).__init__()\n",
|
||||
" self.state_size = state_size\n",
|
||||
" self.action_size = action_size\n",
|
||||
" self.linear1 = torch.nn.Linear(self.state_size, 128)\n",
|
||||
" self.linear2 = torch.nn.Linear(128, 256)\n",
|
||||
" self.linear3 = torch.nn.Linear(256, 1)\n",
|
||||
"\n",
|
||||
" def forward(self, state):\n",
|
||||
" output = F.relu(self.linear1(state))\n",
|
||||
" output = F.relu(self.linear2(output))\n",
|
||||
" value = self.linear3(output)\n",
|
||||
" return value"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"我們需要稍微修改我們的 `discounted_rewards` 和 `run_episode` 函數:\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"def discounted_rewards(next_value, rewards, masks, gamma=0.99):\n",
|
||||
" R = next_value\n",
|
||||
" returns = []\n",
|
||||
" for step in reversed(range(len(rewards))):\n",
|
||||
" R = rewards[step] + gamma * R * masks[step]\n",
|
||||
" returns.insert(0, R)\n",
|
||||
" return returns\n",
|
||||
"\n",
|
||||
"def run_episode(actor, critic, n_iters):\n",
|
||||
" optimizerA = torch.optim.Adam(actor.parameters())\n",
|
||||
" optimizerC = torch.optim.Adam(critic.parameters())\n",
|
||||
" for iter in range(n_iters):\n",
|
||||
" state = env.reset()\n",
|
||||
" log_probs = []\n",
|
||||
" values = []\n",
|
||||
" rewards = []\n",
|
||||
" masks = []\n",
|
||||
" entropy = 0\n",
|
||||
" env.reset()\n",
|
||||
"\n",
|
||||
" for i in count():\n",
|
||||
" env.render()\n",
|
||||
" state = torch.FloatTensor(state).to(device)\n",
|
||||
" dist, value = actor(state), critic(state)\n",
|
||||
"\n",
|
||||
" action = dist.sample()\n",
|
||||
" next_state, reward, done, _ = env.step(action.cpu().numpy())\n",
|
||||
"\n",
|
||||
" log_prob = dist.log_prob(action).unsqueeze(0)\n",
|
||||
" entropy += dist.entropy().mean()\n",
|
||||
"\n",
|
||||
" log_probs.append(log_prob)\n",
|
||||
" values.append(value)\n",
|
||||
" rewards.append(torch.tensor([reward], dtype=torch.float, device=device))\n",
|
||||
" masks.append(torch.tensor([1-done], dtype=torch.float, device=device))\n",
|
||||
"\n",
|
||||
" state = next_state\n",
|
||||
"\n",
|
||||
" if done:\n",
|
||||
" print('Iteration: {}, Score: {}'.format(iter, i))\n",
|
||||
" break\n",
|
||||
"\n",
|
||||
"\n",
|
||||
" next_state = torch.FloatTensor(next_state).to(device)\n",
|
||||
" next_value = critic(next_state)\n",
|
||||
" returns = discounted_rewards(next_value, rewards, masks)\n",
|
||||
"\n",
|
||||
" log_probs = torch.cat(log_probs)\n",
|
||||
" returns = torch.cat(returns).detach()\n",
|
||||
" values = torch.cat(values)\n",
|
||||
"\n",
|
||||
" advantage = returns - values\n",
|
||||
"\n",
|
||||
" actor_loss = -(log_probs * advantage.detach()).mean()\n",
|
||||
" critic_loss = advantage.pow(2).mean()\n",
|
||||
"\n",
|
||||
" optimizerA.zero_grad()\n",
|
||||
" optimizerC.zero_grad()\n",
|
||||
" actor_loss.backward()\n",
|
||||
" critic_loss.backward()\n",
|
||||
" optimizerA.step()\n",
|
||||
" optimizerC.step()\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"現在我們將運行主要訓練循環。我們將通過計算適當的損失函數並更新網絡參數來使用手動網絡訓練過程:\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"\n",
|
||||
"actor = Actor(state_size, action_size).to(device)\n",
|
||||
"critic = Critic(state_size, action_size).to(device)\n",
|
||||
"run_episode(actor, critic, n_iters=100)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": []
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"env.close()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## 重點\n",
|
||||
"\n",
|
||||
"在這個示範中,我們看到了兩種強化學習算法:簡單的策略梯度和更複雜的演員-評論家方法。你可以看到這些算法是基於狀態、行動和獎勵的抽象概念運作的,因此它們可以應用於非常不同的環境。\n",
|
||||
"\n",
|
||||
"強化學習讓我們只需通過觀察最終的獎勵,就能學習解決問題的最佳策略。不需要標籤數據集的特性,讓我們可以多次重複模擬來優化模型。然而,強化學習仍然面臨許多挑戰,如果你決定更深入研究這個有趣的人工智能領域,可能會學到更多相關知識。\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"\n---\n\n**免責聲明**: \n本文件已使用人工智能翻譯服務 [Co-op Translator](https://github.com/Azure/co-op-translator) 進行翻譯。儘管我們致力於提供準確的翻譯,但請注意,自動翻譯可能包含錯誤或不準確之處。原始語言的文件應被視為權威來源。對於重要資訊,建議使用專業人工翻譯。我們對因使用此翻譯而引起的任何誤解或錯誤解釋概不負責。\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"kernelspec": {
|
||||
"display_name": "Python 3.10.4 64-bit",
|
||||
"language": "python",
|
||||
"name": "python3"
|
||||
},
|
||||
"language_info": {
|
||||
"codemirror_mode": {
|
||||
"name": "ipython",
|
||||
"version": 3
|
||||
},
|
||||
"file_extension": ".py",
|
||||
"mimetype": "text/x-python",
|
||||
"name": "python",
|
||||
"nbconvert_exporter": "python",
|
||||
"pygments_lexer": "ipython3",
|
||||
"version": "3.10.4"
|
||||
},
|
||||
"orig_nbformat": 4,
|
||||
"vscode": {
|
||||
"interpreter": {
|
||||
"hash": "916dbcbb3f70747c44a77c7bcd40155683ae19c65e1c03b4aa3499c5328201f1"
|
||||
}
|
||||
},
|
||||
"coopTranslator": {
|
||||
"original_hash": "04f8d9978cd11281d81dd037cbf6ce20",
|
||||
"translation_date": "2025-08-31T09:26:45+00:00",
|
||||
"source_file": "lessons/6-Other/22-DeepRL/CartPole-RL-PyTorch.ipynb",
|
||||
"language_code": "hk"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 2
|
||||
}
|
||||
File diff suppressed because one or more lines are too long
|
|
@ -0,0 +1,117 @@
|
|||
# 深度強化學習
|
||||
|
||||
強化學習 (RL) 被視為機器學習的基本範式之一,與監督學習和非監督學習並列。在監督學習中,我們依賴已知結果的數據集,而強化學習則基於**透過行動學習**。例如,當我們第一次看到一個電腦遊戲時,即使不知道規則,我們也會開始玩,並且很快就能通過玩遊戲和調整行為來提升技能。
|
||||
|
||||
## [課前測驗](https://ff-quizzes.netlify.app/en/ai/quiz/43)
|
||||
|
||||
要進行強化學習,我們需要:
|
||||
|
||||
* 一個**環境**或**模擬器**,用來設定遊戲規則。我們應該能夠在模擬器中運行實驗並觀察結果。
|
||||
* 某種**獎勵函數**,用來指示我們的實驗是否成功。以學習玩電腦遊戲為例,獎勵可以是我們的最終得分。
|
||||
|
||||
基於獎勵函數,我們應該能夠調整行為並提升技能,以便下一次表現更好。強化學習與其他類型的機器學習的主要區別在於,強化學習通常直到遊戲結束才知道是否贏或輸。因此,我們無法判斷某個單獨的動作是否是好的——只有在遊戲結束時才會收到獎勵。
|
||||
|
||||
在強化學習過程中,我們通常會進行許多實驗。在每次實驗中,我們需要在遵循目前學到的最佳策略(**利用**)和探索新的可能狀態(**探索**)之間取得平衡。
|
||||
|
||||
## OpenAI Gym
|
||||
|
||||
一個非常適合強化學習的工具是 [OpenAI Gym](https://gym.openai.com/)——一個**模擬環境**,可以模擬許多不同的環境,從 Atari 遊戲到物理學中的桿平衡。它是訓練強化學習算法最受歡迎的模擬環境之一,由 [OpenAI](https://openai.com/) 維護。
|
||||
|
||||
> **注意**: 你可以在 [這裡](https://gym.openai.com/envs/#classic_control) 查看 OpenAI Gym 提供的所有環境。
|
||||
|
||||
## CartPole 平衡
|
||||
|
||||
你可能都見過現代的平衡裝置,例如 *Segway* 或 *Gyroscooters*。它們能夠通過加速計或陀螺儀的信號自動調整輪子來保持平衡。在本節中,我們將學習如何解決類似的問題——平衡一根桿子。這類似於馬戲團表演者需要在手上平衡桿子的情況——但這裡的桿平衡僅發生在一維空間。
|
||||
|
||||
一個簡化版的平衡問題被稱為 **CartPole** 問題。在 CartPole 世界中,我們有一個可以左右移動的水平滑塊,目標是讓滑塊上的垂直桿保持平衡。
|
||||
|
||||
<img alt="a cartpole" src="../../../../../translated_images/zh-HK/cartpole.f52a67f27e058170.webp" width="200"/>
|
||||
|
||||
要創建並使用這個環境,我們只需要幾行 Python 代碼:
|
||||
|
||||
```python
|
||||
import gym
|
||||
env = gym.make("CartPole-v1")
|
||||
|
||||
env.reset()
|
||||
done = False
|
||||
total_reward = 0
|
||||
while not done:
|
||||
env.render()
|
||||
action = env.action_space.sample()
|
||||
observaton, reward, done, info = env.step(action)
|
||||
total_reward += reward
|
||||
|
||||
print(f"Total reward: {total_reward}")
|
||||
```
|
||||
|
||||
每個環境都可以以相同的方式訪問:
|
||||
* `env.reset` 開始一個新的實驗
|
||||
* `env.step` 執行一個模擬步驟。它接收來自**動作空間**的**動作**,並返回**觀察值**(來自觀察空間)、獎勵以及終止標誌。
|
||||
|
||||
在上面的例子中,我們在每一步執行隨機動作,因此實驗的生命週期非常短:
|
||||
|
||||

|
||||
|
||||
強化學習算法的目標是訓練一個模型——即所謂的**策略** π——它能根據給定的狀態返回動作。我們也可以將策略視為概率性的,例如,對於任何狀態 *s* 和動作 *a*,它會返回 π(*a*|*s*) 的概率,表示在狀態 *s* 下應該採取動作 *a*。
|
||||
|
||||
## 策略梯度算法
|
||||
|
||||
建模策略最明顯的方法是創建一個神經網絡,該網絡以狀態作為輸入,並返回相應的動作(或者更準確地說是所有動作的概率)。在某種意義上,它類似於普通的分類任務,但有一個主要區別——我們事先不知道在每一步應該採取哪些動作。
|
||||
|
||||
這裡的想法是估算這些概率。我們構建一個**累積獎勵**向量,顯示實驗中每一步的總獎勵。我們還通過乘以某個係數 γ=0.99 來應用**獎勵折扣**,以減少早期獎勵的影響。然後,我們加強那些在實驗路徑中產生較大獎勵的步驟。
|
||||
|
||||
> 了解更多關於策略梯度算法的內容,並在[示例筆記本](CartPole-RL-TF.ipynb)中查看其實際應用。
|
||||
|
||||
## Actor-Critic 算法
|
||||
|
||||
策略梯度方法的一個改進版本被稱為 **Actor-Critic**。其主要思想是神經網絡將被訓練以返回兩個結果:
|
||||
|
||||
* 策略,決定應採取的動作。這部分被稱為 **actor**。
|
||||
* 對當前狀態下可期望獲得的總獎勵的估算——這部分被稱為 **critic**。
|
||||
|
||||
在某種意義上,這種架構類似於 [GAN](../../4-ComputerVision/10-GANs/README.md),其中有兩個網絡相互對抗訓練。在 Actor-Critic 模型中,actor 提出需要採取的動作,而 critic 則進行批判並估算結果。然而,我們的目標是讓這兩個網絡協同訓練。
|
||||
|
||||
由於我們在實驗中知道真實的累積獎勵以及 critic 返回的結果,因此構建一個損失函數來最小化它們之間的差異相對容易。這將給我們**critic 損失**。我們可以使用與策略梯度算法相同的方法計算**actor 損失**。
|
||||
|
||||
運行其中一個算法後,我們可以期望 CartPole 的表現如下:
|
||||
|
||||

|
||||
|
||||
## ✍️ 練習:策略梯度和 Actor-Critic 強化學習
|
||||
|
||||
在以下筆記本中繼續學習:
|
||||
|
||||
* [TensorFlow 中的強化學習](CartPole-RL-TF.ipynb)
|
||||
* [PyTorch 中的強化學習](CartPole-RL-PyTorch.ipynb)
|
||||
|
||||
## 其他強化學習任務
|
||||
|
||||
如今,強化學習是一個快速增長的研究領域。一些有趣的強化學習例子包括:
|
||||
|
||||
* 教電腦玩 **Atari 遊戲**。這個問題的挑戰在於我們沒有簡單的狀態表示為向量,而是截圖——我們需要使用 CNN 將屏幕圖像轉換為特徵向量或提取獎勵信息。Atari 遊戲可在 Gym 中使用。
|
||||
* 教電腦玩棋盤遊戲,例如象棋和圍棋。最近,像 **Alpha Zero** 這樣的最先進程序是通過兩個代理相互對弈並在每一步中改進來從零開始訓練的。
|
||||
* 在工業中,強化學習被用於從模擬中創建控制系統。一項名為 [Bonsai](https://azure.microsoft.com/services/project-bonsai/?WT.mc_id=academic-77998-cacaste) 的服務專門為此設計。
|
||||
|
||||
## 結論
|
||||
|
||||
我們現在已經學會如何僅通過提供定義遊戲期望狀態的獎勵函數,並給予智能探索搜索空間的機會來訓練代理以取得良好結果。我們成功嘗試了兩種算法,並在相對短的時間內取得了良好的結果。然而,這只是你進入強化學習旅程的開始,如果你想深入研究,應該考慮參加專門的課程。
|
||||
|
||||
## 🚀 挑戰
|
||||
|
||||
探索「其他強化學習任務」部分列出的應用,並嘗試實現其中一個!
|
||||
|
||||
## [課後測驗](https://ff-quizzes.netlify.app/en/ai/quiz/44)
|
||||
|
||||
## 回顧與自學
|
||||
|
||||
在我們的 [機器學習初學者課程](https://github.com/microsoft/ML-For-Beginners/blob/main/8-Reinforcement/README.md) 中了解更多關於經典強化學習的內容。
|
||||
|
||||
觀看 [這段精彩視頻](https://www.youtube.com/watch?v=qv6UVOQ0F44),了解電腦如何學習玩超級馬里奧。
|
||||
|
||||
## 作業:[訓練一輛山地車](lab/README.md)
|
||||
|
||||
在這次作業中,你的目標是訓練一個不同的 Gym 環境——[山地車](https://www.gymlibrary.ml/environments/classic_control/mountain_car/)。
|
||||
|
||||
---
|
||||
|
||||
|
|
@ -0,0 +1,109 @@
|
|||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"# 訓練山地車逃脫\n",
|
||||
"\n",
|
||||
"來自 [AI for Beginners Curriculum](https://github.com/microsoft/ai-for-beginners) 的實驗作業。\n",
|
||||
"\n",
|
||||
"你的目標是訓練強化學習代理控制 OpenAI 環境中的 [Mountain Car](https://www.gymlibrary.ml/environments/classic_control/mountain_car/)。\n",
|
||||
"\n",
|
||||
"讓我們從創建環境開始:\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 1,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import gym\n",
|
||||
"env = gym.make('MountainCar-v0')"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"讓我們看看隨機實驗是什麼樣子:\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 2,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"state = env.reset()\n",
|
||||
"while True:\n",
|
||||
" env.render()\n",
|
||||
" action = env.action_space.sample()\n",
|
||||
" state, reward, done, info = env.step(action)\n",
|
||||
" if done:\n",
|
||||
" break"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": []
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"## Lost of code here"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 3,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"env.close()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"\n---\n\n**免責聲明**: \n本文件已使用人工智能翻譯服務 [Co-op Translator](https://github.com/Azure/co-op-translator) 進行翻譯。儘管我們致力於提供準確的翻譯,但請注意,自動翻譯可能包含錯誤或不準確之處。原始文件的母語版本應被視為權威來源。對於重要信息,建議使用專業人工翻譯。我們對因使用此翻譯而引起的任何誤解或錯誤解釋概不負責。\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"interpreter": {
|
||||
"hash": "16af2a8bbb083ea23e5e41c7f5787656b2ce26968575d8763f2c4b17f9cd711f"
|
||||
},
|
||||
"kernelspec": {
|
||||
"display_name": "Python 3.8.12 ('py38')",
|
||||
"language": "python",
|
||||
"name": "python3"
|
||||
},
|
||||
"language_info": {
|
||||
"codemirror_mode": {
|
||||
"name": "ipython",
|
||||
"version": 3
|
||||
},
|
||||
"file_extension": ".py",
|
||||
"mimetype": "text/x-python",
|
||||
"name": "python",
|
||||
"nbconvert_exporter": "python",
|
||||
"pygments_lexer": "ipython3",
|
||||
"version": "3.8.12"
|
||||
},
|
||||
"coopTranslator": {
|
||||
"original_hash": "f062b3b18449593ef8e0fcc029868781",
|
||||
"translation_date": "2025-08-31T09:29:01+00:00",
|
||||
"source_file": "lessons/6-Other/22-DeepRL/lab/MountainCar.ipynb",
|
||||
"language_code": "hk"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 4
|
||||
}
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
## 環境介紹
|
||||
|
||||
Mountain Car 環境由一輛被困在山谷中的車組成。你的目標是跳出山谷並到達旗幟的位置。你可以執行的動作包括向左加速、向右加速,或者什麼都不做。你可以觀察到車在 x 軸上的位置以及速度。
|
||||
|
||||
## 開始筆記本
|
||||
|
||||
通過打開 [MountainCar.ipynb](../../../../../../lessons/6-Other/22-DeepRL/lab/MountainCar.ipynb) 開始這個實驗。
|
||||
|
||||
## 重點
|
||||
|
||||
在這個實驗中,你應該學到將 RL 演算法應用到一個新環境通常是相當直接的,因為 OpenAI Gym 的所有環境都具有相同的介面,而演算法本身並不會過多依賴於環境的特性。你甚至可以重新結構化 Python 程式碼,以便將任何環境作為參數傳遞給 RL 演算法。
|
||||
|
||||
**免責聲明**:
|
||||
本文件已使用人工智能翻譯服務 [Co-op Translator](https://github.com/Azure/co-op-translator) 進行翻譯。我們致力於提供準確的翻譯,但請注意,自動翻譯可能包含錯誤或不準確之處。應以原文文件作為權威來源。對於關鍵資訊,建議尋求專業人工翻譯。我們對因使用此翻譯而引起的任何誤解或誤釋不承擔責任。
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
|
@ -0,0 +1,155 @@
|
|||
# 多代理系統
|
||||
|
||||
實現智能的一種可能方法是所謂的**湧現**(或**協同**)方法,這基於以下事實:許多相對簡單的代理的組合行為可以導致整個系統的更複雜(或智能)行為。理論上,這基於[集體智慧](https://en.wikipedia.org/wiki/Collective_intelligence)、[湧現主義](https://en.wikipedia.org/wiki/Global_brain)和[進化控制論](https://en.wikipedia.org/wiki/Global_brain)的原則,這些原則指出,當低層系統被適當地組合成高層系統時,會產生某種附加價值(即所謂的*元系統轉換原則*)。
|
||||
|
||||
## [課前測驗](https://ff-quizzes.netlify.app/en/ai/quiz/45)
|
||||
|
||||
**多代理系統**的方向在1990年代因互聯網和分佈式系統的增長而在人工智能領域中興起。經典的人工智能教科書之一[《人工智能:現代方法》](https://en.wikipedia.org/wiki/Artificial_Intelligence:_A_Modern_Approach)從多代理系統的角度探討了經典人工智能。
|
||||
|
||||
多代理方法的核心是**代理**的概念——一個生活在某個**環境**中的實體,它可以感知並對環境進行操作。這是一個非常廣泛的定義,代理可以有許多不同的類型和分類:
|
||||
|
||||
* 按其推理能力:
|
||||
- **反應型**代理通常具有簡單的請求-響應類型行為
|
||||
- **推理型**代理使用某種邏輯推理和/或規劃能力
|
||||
* 按代理執行代碼的位置:
|
||||
- **靜態**代理在專用的網絡節點上工作
|
||||
- **移動**代理可以在網絡節點之間移動其代碼
|
||||
* 按其行為:
|
||||
- **被動代理**沒有特定目標。這些代理可以對外部刺激作出反應,但不會主動採取行動。
|
||||
- **主動代理**有一些目標需要追求
|
||||
- **認知代理**涉及複雜的規劃和推理
|
||||
|
||||
如今,多代理系統被應用於許多領域:
|
||||
|
||||
* 在遊戲中,許多非玩家角色使用某種人工智能,可以被視為智能代理
|
||||
* 在視頻製作中,渲染涉及人群的複雜3D場景通常使用多代理模擬
|
||||
* 在系統建模中,多代理方法用於模擬複雜模型的行為。例如,多代理方法已成功用於預測COVID-19疾病在全球的傳播。類似的方法可以用於模擬城市交通,並觀察其對交通規則變化的反應。
|
||||
* 在複雜的自動化系統中,每個設備可以作為獨立的代理,這使得整個系統不再是單一的,並且更加穩健。
|
||||
|
||||
我們不會深入探討多代理系統,但會考慮一個**多代理建模**的例子。
|
||||
|
||||
## NetLogo
|
||||
|
||||
[NetLogo](https://ccl.northwestern.edu/netlogo/)是一個基於修改版[Logo](https://en.wikipedia.org/wiki/Logo_(programming_language))編程語言的多代理建模環境。這種語言是為了教孩子編程概念而開發的,它允許你控制一個名為**烏龜**的代理,該代理可以移動並留下痕跡。這使得創建複雜的幾何圖形成為可能,這是一種非常直觀的方式來理解代理的行為。
|
||||
|
||||
在NetLogo中,我們可以使用`create-turtles`命令創建許多烏龜。然後我們可以命令所有烏龜執行一些動作(例如下面的例子中向前移動10點):
|
||||
|
||||
```
|
||||
create-turtles 10
|
||||
ask turtles [
|
||||
forward 10
|
||||
]
|
||||
```
|
||||
|
||||
當然,所有烏龜都做同樣的事情並不有趣,因此我們可以`ask`某些烏龜群體,例如那些在某個點附近的烏龜。我們還可以使用`breed [cats cat]`命令創建不同*品種*的烏龜。這裡`cat`是品種的名稱,我們需要指定單數和複數形式,因為不同的命令使用不同的形式以提高清晰度。
|
||||
|
||||
> ✅ 我們不會深入學習NetLogo語言本身——如果你有興趣了解更多,可以訪問出色的[初學者互動NetLogo詞典](https://ccl.northwestern.edu/netlogo/bind/)資源。
|
||||
|
||||
你可以[下載](https://ccl.northwestern.edu/netlogo/download.shtml)並安裝NetLogo來試用。
|
||||
|
||||
### 模型庫
|
||||
|
||||
NetLogo的一大優勢是它包含一個可供試用的工作模型庫。進入**File → Models Library**,你可以選擇許多類別的模型。
|
||||
|
||||
<img alt="NetLogo模型庫" src="../../../../../translated_images/zh-HK/NetLogo-ModelLib.efe023afb4763c05.webp" width="60%"/>
|
||||
|
||||
> Dmitry Soshnikov提供的模型庫截圖
|
||||
|
||||
你可以打開其中一個模型,例如**Biology → Flocking**。
|
||||
|
||||
### 主要原則
|
||||
|
||||
打開模型後,你會進入NetLogo的主屏幕。以下是一個描述狼和羊的種群模型,給定有限資源(草地)。
|
||||
|
||||

|
||||
|
||||
> Dmitry Soshnikov提供的截圖
|
||||
|
||||
在這個屏幕上,你可以看到:
|
||||
|
||||
* **界面**部分包含:
|
||||
- 所有代理生活的主場地
|
||||
- 不同的控制項:按鈕、滑塊等
|
||||
- 用於顯示模擬參數的圖表
|
||||
* **代碼**標籤包含編輯器,你可以在其中輸入NetLogo程序
|
||||
|
||||
在大多數情況下,界面會有一個**Setup**按鈕,用於初始化模擬狀態,以及一個**Go**按鈕,用於開始執行。這些由代碼中的相應處理程序處理,看起來像這樣:
|
||||
|
||||
```
|
||||
to go [
|
||||
...
|
||||
]
|
||||
```
|
||||
|
||||
NetLogo的世界由以下對象組成:
|
||||
|
||||
* **代理**(烏龜)可以在場地上移動並執行某些操作。你可以使用`ask turtles [...]`語法命令代理,括號中的代碼由所有代理以*烏龜模式*執行。
|
||||
* **補丁**是場地上的方形區域,代理生活在其上。你可以引用同一補丁上的所有代理,或者更改補丁顏色和其他屬性。你也可以`ask patches`執行某些操作。
|
||||
* **觀察者**是一個獨特的代理,控制整個世界。所有按鈕處理程序都以*觀察者模式*執行。
|
||||
|
||||
> ✅ 多代理環境的美妙之處在於,烏龜模式或補丁模式下運行的代碼由所有代理同時並行執行。因此,通過編寫少量代碼並編程個別代理的行為,你可以創建整個模擬系統的複雜行為。
|
||||
|
||||
### 群聚行為
|
||||
|
||||
作為多代理行為的一個例子,我們來看看**[群聚行為](https://en.wikipedia.org/wiki/Flocking_(behavior))**。群聚是一種複雜的模式,非常類似於鳥群的飛行方式。觀察它們飛行時,你可能會認為它們遵循某種集體算法,或者擁有某種*集體智慧*。然而,這種複雜行為的產生是因為每個個體代理(在這種情況下是一隻*鳥*)僅觀察距離它很近的其他代理,並遵循三個簡單的規則:
|
||||
|
||||
* **對齊** - 它向鄰近代理的平均方向靠攏
|
||||
* **凝聚** - 它試圖向鄰居的平均位置靠攏(*長距離吸引*)
|
||||
* **分離** - 當與其他鳥靠得太近時,它試圖遠離(*短距離排斥*)
|
||||
|
||||
你可以運行群聚行為的例子並觀察其行為。你還可以調整參數,例如*分離程度*或*視距範圍*,這定義了每隻鳥能看到多遠。注意,如果你將視距範圍減少到0,所有鳥都變得盲目,群聚行為停止。如果你將分離減少到0,所有鳥會聚集成一條直線。
|
||||
|
||||
> ✅ 切換到**代碼**標籤,查看群聚行為的三個規則(對齊、凝聚和分離)如何在代碼中實現。注意我們僅引用那些在視線範圍內的代理。
|
||||
|
||||
### 其他可查看的模型
|
||||
|
||||
還有一些有趣的模型可以供你實驗:
|
||||
|
||||
* **Art → Fireworks**展示了如何將煙花視為個別火流的集體行為
|
||||
* **Social Science → Traffic Basic**和**Social Science → Traffic Grid**展示了城市交通在1D和2D網格中有或沒有交通燈的模型。模擬中的每輛車遵循以下規則:
|
||||
- 如果前方空間是空的——加速(直到某個最大速度)
|
||||
- 如果看到前方有障礙物——剎車(你可以調整司機能看到多遠)
|
||||
* **Social Science → Party**展示了人們在雞尾酒會上如何聚集在一起。你可以找到能最快提升群體幸福感的參數組合。
|
||||
|
||||
從這些例子中可以看出,多代理模擬是一種非常有用的方法,可以理解由遵循相同或相似邏輯的個體組成的複雜系統的行為。它還可以用於控制虛擬代理,例如電腦遊戲中的[NPC](https://en.wikipedia.org/wiki/NPC),或3D動畫世界中的代理。
|
||||
|
||||
## 推理型代理
|
||||
|
||||
上述代理非常簡單,使用某種算法對環境的變化作出反應。因此,它們是**反應型代理**。然而,有時代理可以進行推理並規劃其行動,在這種情況下,它們被稱為**推理型代理**。
|
||||
|
||||
一個典型的例子是個人代理接收到人類的指令去預訂度假行程。假設互聯網上有許多代理可以幫助它。它應該聯繫其他代理以查看哪些航班可用,不同日期的酒店價格是多少,並嘗試談判最佳價格。當度假計劃完成並得到主人確認後,它可以進行預訂。
|
||||
|
||||
為了做到這一點,代理需要**通信**。為了成功通信,它們需要:
|
||||
|
||||
* 一些**交換知識的標準語言**,例如[知識交換格式](https://en.wikipedia.org/wiki/Knowledge_Interchange_Format)(KIF)和[知識查詢與操作語言](https://en.wikipedia.org/wiki/Knowledge_Query_and_Manipulation_Language)(KQML)。這些語言基於[言語行為理論](https://en.wikipedia.org/wiki/Speech_act)設計。
|
||||
* 這些語言還應包括一些**談判協議**,基於不同的**拍賣類型**。
|
||||
* 一個**共同的本體**,以便它們引用相同的概念並了解其語義
|
||||
* 一種**發現**不同代理能做什麼的方法,也基於某種本體
|
||||
|
||||
推理型代理比反應型代理更複雜,因為它們不僅對環境的變化作出反應,還應能夠*主動*採取行動。一種提出的推理型代理架構是所謂的信念-願望-意圖(BDI)代理:
|
||||
|
||||
* **信念**構成代理對其環境的知識集。它可以結構化為知識庫或代理可以應用於環境中特定情況的一組規則。
|
||||
* **願望**定義代理想要做什麼,即其目標。例如,上述個人助理代理的目標是預訂行程,而酒店代理的目標是最大化利潤。
|
||||
* **意圖**是代理為實現其目標而計劃的具體行動。行動通常改變環境並導致與其他代理的通信。
|
||||
|
||||
有一些平台可用於構建多代理系統,例如[JADE](https://jade.tilab.com/)。[這篇論文](https://arxiv.org/ftp/arxiv/papers/2007/2007.08961.pdf)包含了多代理平台的回顧,以及多代理系統的簡史及其不同的使用場景。
|
||||
|
||||
## 結論
|
||||
|
||||
多代理系統可以採取非常不同的形式並應用於許多不同的領域。
|
||||
它們都傾向於專注於個別代理的簡單行為,並通過**協同效應**實現整個系統的更複雜行為。
|
||||
|
||||
## 🚀 挑戰
|
||||
|
||||
將這節課的內容應用於現實世界,嘗試構思一個可以解決問題的多代理系統。例如,多代理系統需要做什麼才能優化校車路線?它如何在麵包店中運作?
|
||||
|
||||
## [課後測驗](https://ff-quizzes.netlify.app/en/ai/quiz/46)
|
||||
|
||||
## 回顧與自學
|
||||
|
||||
回顧這類系統在工業中的應用。選擇一個領域,例如製造業或視頻遊戲行業,探索多代理系統如何用於解決獨特的問題。
|
||||
|
||||
## [NetLogo作業](assignment.md)
|
||||
|
||||
---
|
||||
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
# NetLogo 作業
|
||||
|
||||
選擇 NetLogo 資料庫中的其中一個模型,並使用它來盡可能模擬一個真實生活中的情境。一個不錯的例子是調整「替代視覺化」資料夾中的病毒模型,展示它如何用於模擬 COVID-19 的傳播。你能否建立一個模擬真實病毒傳播的模型?
|
||||
|
||||
透過保存副本並製作一段視頻演示,解釋該模型如何與真實世界情境相關,來展示你的成果。
|
||||
|
||||
**免責聲明**:
|
||||
本文件使用人工智能翻譯服務 [Co-op Translator](https://github.com/Azure/co-op-translator) 進行翻譯。我們致力於提供準確的翻譯,但請注意,自動翻譯可能包含錯誤或不準確之處。應以原文文件作為權威來源。對於關鍵資訊,建議尋求專業人工翻譯。我們對因使用此翻譯而引起的任何誤解或誤釋概不負責。
|
||||
|
|
@ -0,0 +1,43 @@
|
|||
# 道德與負責任的人工智能
|
||||
|
||||
你已經快完成這門課程了,希望到目前為止,你已清楚了解到人工智能是基於一系列正式的數學方法,這些方法能幫助我們在數據中找到關係,並訓練模型來模仿某些人類行為。在這個歷史時刻,我們認為人工智能是一個非常強大的工具,可以從數據中提取模式,並應用這些模式來解決新的問題。
|
||||
|
||||
## [課前測驗](https://white-water-09ec41f0f.azurestaticapps.net/quiz/5/)
|
||||
|
||||
然而,在科幻作品中,我們經常看到人工智能對人類構成威脅的故事。通常這些故事圍繞某種人工智能叛變展開,當人工智能決定與人類對抗時,這暗示人工智能擁有某種情感或能做出開發者未預見的決定。
|
||||
|
||||
我們在這門課程中學到的人工智能僅僅是大型矩陣運算。它是一個非常強大的工具,可以幫助我們解決問題,但和其他任何強大的工具一樣——它既可以用於好的目的,也可以用於壞的目的。重要的是,它可能會被*濫用*。
|
||||
|
||||
## 負責任人工智能的原則
|
||||
|
||||
為了避免人工智能的意外或故意濫用,Microsoft 提出了重要的[負責任人工智能原則](https://www.microsoft.com/ai/responsible-ai?WT.mc_id=academic-77998-cacaste)。以下概念是這些原則的基礎:
|
||||
|
||||
* **公平性**與*模型偏差*這個重要問題相關,模型偏差可能是由於使用了有偏差的數據進行訓練。例如,當我們嘗試預測某人獲得軟件開發工作機會的概率時,模型可能更偏向男性——僅僅因為訓練數據集可能偏向男性群體。我們需要仔細平衡訓練數據並調查模型以避免偏差,確保模型考慮到更相關的特徵。
|
||||
* **可靠性與安全性**。人工智能模型本質上可能會犯錯。一個神經網絡返回的是概率,我們需要在做決策時考慮到這一點。每個模型都有一定的精確度和召回率,我們需要理解這些指標以防止錯誤建議可能造成的傷害。
|
||||
* **隱私與安全性**有一些人工智能特定的影響。例如,當我們使用某些數據來訓練模型時,這些數據會以某種方式“整合”到模型中。一方面,這提高了安全性和隱私性,另一方面——我們需要記住模型是基於哪些數據訓練的。
|
||||
* **包容性**意味著我們不是在構建人工智能來取代人類,而是為了增強人類並使我們的工作更具創造性。這也與公平性相關,因為在處理代表性不足的社群時,我們收集的大多數數據集可能存在偏差,我們需要確保這些社群被納入並正確地由人工智能處理。
|
||||
* **透明性**。這包括確保我們始終清楚地表明人工智能正在被使用。此外,在可能的情況下,我們希望使用*可解釋*的人工智能系統。
|
||||
* **問責性**。當人工智能模型做出某些決定時,並不總是清楚誰應對這些決定負責。我們需要確保我們理解人工智能決策的責任所在。在大多數情況下,我們希望將人類納入重要決策的過程中,以便實際的人對決策負責。
|
||||
|
||||
## 負責任人工智能的工具
|
||||
|
||||
Microsoft 開發了[負責任人工智能工具箱](https://github.com/microsoft/responsible-ai-toolbox),其中包含一系列工具:
|
||||
|
||||
* 解釋性儀表板 (InterpretML)
|
||||
* 公平性儀表板 (FairLearn)
|
||||
* 錯誤分析儀表板
|
||||
* 負責任人工智能儀表板,包括:
|
||||
|
||||
- EconML - 用於因果分析的工具,專注於假設性問題
|
||||
- DiCE - 用於反事實分析的工具,讓你了解需要改變哪些特徵才能影響模型的決策
|
||||
|
||||
有關人工智能倫理的更多信息,請訪問[這節課](https://github.com/microsoft/ML-For-Beginners/tree/main/1-Introduction/3-fairness?WT.mc_id=academic-77998-cacaste),它是機器學習課程的一部分,並包含作業。
|
||||
|
||||
## 回顧與自學
|
||||
|
||||
參加這個[學習路徑](https://docs.microsoft.com/learn/modules/responsible-ai-principles/?WT.mc_id=academic-77998-cacaste),以了解更多關於負責任人工智能的內容。
|
||||
|
||||
## [課後測驗](https://white-water-09ec41f0f.azurestaticapps.net/quiz/6/)
|
||||
|
||||
**免責聲明**:
|
||||
本文件已使用人工智能翻譯服務 [Co-op Translator](https://github.com/Azure/co-op-translator) 進行翻譯。我們致力於提供準確的翻譯,但請注意,自動翻譯可能包含錯誤或不準確之處。應以原文文件作為權威來源。對於關鍵資訊,建議尋求專業人工翻譯。我們對因使用此翻譯而引起的任何誤解或誤釋不承擔責任。
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
# 概覽
|
||||
|
||||

|
||||
|
||||
> 手繪筆記由 [Tomomi Imura](https://twitter.com/girlie_mac) 提供
|
||||
|
||||
**免責聲明**:
|
||||
本文件已使用人工智能翻譯服務 [Co-op Translator](https://github.com/Azure/co-op-translator) 進行翻譯。我們致力於提供準確的翻譯,但請注意,自動翻譯可能包含錯誤或不準確之處。應以原文文件作為權威來源。對於關鍵資訊,建議尋求專業人工翻譯。我們對因使用此翻譯而引起的任何誤解或誤釋不承擔責任。
|
||||
File diff suppressed because one or more lines are too long
|
|
@ -0,0 +1,79 @@
|
|||
# 多模態網絡
|
||||
|
||||
在 Transformer 模型成功解決 NLP 任務後,相同或類似的架構也被應用於計算機視覺任務。越來越多的研究致力於構建能夠*結合*視覺和自然語言能力的模型。其中一個嘗試是由 OpenAI 開發的 CLIP 和 DALL.E。
|
||||
|
||||
## 對比圖像預訓練 (CLIP)
|
||||
|
||||
CLIP 的主要理念是能夠比較文本提示與圖像,並判斷圖像與提示的匹配程度。
|
||||
|
||||

|
||||
|
||||
> *圖片來源:[這篇博客文章](https://openai.com/blog/clip/)*
|
||||
|
||||
該模型是基於從互聯網獲取的圖像及其標題進行訓練的。對於每個批次,我們取 N 對 (圖像, 文本),並將它們轉換為一些向量表示 I, ..., T。這些表示隨後會進行匹配。損失函數的定義是最大化同一對(例如 I 和 T)向量之間的餘弦相似度,並最小化所有其他對之間的餘弦相似度。因此,這種方法被稱為**對比式**。
|
||||
|
||||
CLIP 模型/庫可從 [OpenAI GitHub](https://github.com/openai/CLIP) 獲得。該方法在[這篇博客文章](https://openai.com/blog/clip/)中有描述,並在[這篇論文](https://arxiv.org/pdf/2103.00020.pdf)中有更詳細的解釋。
|
||||
|
||||
一旦該模型完成預訓練,我們可以提供一批圖像和一批文本提示,模型將返回一個概率張量。CLIP 可用於多種任務:
|
||||
|
||||
**圖像分類**
|
||||
|
||||
假設我們需要將圖像分類為例如貓、狗和人。在這種情況下,我們可以向模型提供一張圖像,以及一系列文本提示:“*一張貓的照片*”、“*一張狗的照片*”、“*一張人的照片*”。在結果的三個概率向量中,我們只需選擇值最高的索引。
|
||||
|
||||

|
||||
|
||||
> *圖片來源:[這篇博客文章](https://openai.com/blog/clip/)*
|
||||
|
||||
**基於文本的圖像搜索**
|
||||
|
||||
我們也可以進行相反的操作。如果我們有一組圖像,可以將這組圖像傳遞給模型,並提供一個文本提示——這將返回與給定提示最相似的圖像。
|
||||
|
||||
## ✍️ 示例:[使用 CLIP 進行圖像分類和圖像搜索](../../../../../lessons/X-Extras/X1-MultiModal/Clip.ipynb)
|
||||
|
||||
打開 [Clip.ipynb](../../../../../lessons/X-Extras/X1-MultiModal/Clip.ipynb) 筆記本,查看 CLIP 的實際應用。
|
||||
|
||||
## 使用 VQGAN+CLIP 進行圖像生成
|
||||
|
||||
CLIP 還可以用於基於文本提示的**圖像生成**。為了實現這一點,我們需要一個**生成器模型**,能夠基於某些向量輸入生成圖像。其中一個模型是 [VQGAN](https://compvis.github.io/taming-transformers/)(向量量化 GAN)。
|
||||
|
||||
VQGAN 與普通 [GAN](../../4-ComputerVision/10-GANs/README.md) 的主要區別在於以下幾點:
|
||||
* 使用自回歸 Transformer 架構生成一系列具有上下文的視覺部分,這些部分構成了圖像。這些視覺部分由 [CNN](../../4-ComputerVision/07-ConvNets/README.md) 學習。
|
||||
* 使用子圖像判別器來檢測圖像的部分是否“真實”或“虛假”(與傳統 GAN 的“全或無”方法不同)。
|
||||
|
||||
在 [Taming Transformers](https://compvis.github.io/taming-transformers/) 網站上了解更多關於 VQGAN 的信息。
|
||||
|
||||
VQGAN 與傳統 GAN 的一個重要區別是,後者可以從任何輸入向量生成一張像樣的圖像,而 VQGAN 則可能生成不連貫的圖像。因此,我們需要進一步引導圖像創建過程,這可以通過 CLIP 來完成。
|
||||
|
||||

|
||||
|
||||
為了生成與文本提示相對應的圖像,我們首先使用一些隨機編碼向量,通過 VQGAN 生成一張圖像。然後使用 CLIP 生成損失函數,顯示圖像與文本提示的匹配程度。目標是最小化該損失,通過反向傳播調整輸入向量的參數。
|
||||
|
||||
一個實現 VQGAN+CLIP 的優秀庫是 [Pixray](http://github.com/pixray/pixray)。
|
||||
|
||||
 |  | 
|
||||
----|----|----
|
||||
根據提示 *一張年輕男性文學教師拿著書的水彩特寫肖像* 生成的圖片 | 根據提示 *一張年輕女性計算機科學教師拿著電腦的油畫特寫肖像* 生成的圖片 | 根據提示 *一張老年男性數學教師站在黑板前的油畫特寫肖像* 生成的圖片
|
||||
|
||||
> 圖片來自 **人工教師** 系列,由 [Dmitry Soshnikov](http://soshnikov.com) 創作
|
||||
|
||||
## DALL-E
|
||||
### [DALL-E 1](https://openai.com/research/dall-e)
|
||||
DALL-E 是 GPT-3 的一個版本,訓練用於根據提示生成圖像。它擁有 120 億個參數。
|
||||
|
||||
與 CLIP 不同,DALL-E 將文本和圖像作為一個單一的令牌流進行處理。因此,通過多個提示,可以根據文本生成圖像。
|
||||
|
||||
### [DALL-E 2](https://openai.com/dall-e-2)
|
||||
DALL-E 1 和 DALL-E 2 的主要區別在於,後者能生成更逼真的圖像和藝術作品。
|
||||
|
||||
以下是使用 DALL-E 生成的圖像示例:
|
||||
 |  | 
|
||||
----|----|----
|
||||
根據提示 *一張年輕男性文學教師拿著書的水彩特寫肖像* 生成的圖片 | 根據提示 *一張年輕女性計算機科學教師拿著電腦的油畫特寫肖像* 生成的圖片 | 根據提示 *一張老年男性數學教師站在黑板前的油畫特寫肖像* 生成的圖片
|
||||
|
||||
## 參考資料
|
||||
|
||||
* VQGAN 論文:[Taming Transformers for High-Resolution Image Synthesis](https://compvis.github.io/taming-transformers/paper/paper.pdf)
|
||||
* CLIP 論文:[Learning Transferable Visual Models From Natural Language Supervision](https://arxiv.org/pdf/2103.00020.pdf)
|
||||
|
||||
**免責聲明**:
|
||||
本文件使用人工智能翻譯服務 [Co-op Translator](https://github.com/Azure/co-op-translator) 進行翻譯。我們致力於提供準確的翻譯,但請注意,自動翻譯可能包含錯誤或不準確之處。應以原文文件作為權威來源。對於關鍵資訊,建議尋求專業人工翻譯。我們對因使用此翻譯而引起的任何誤解或誤釋不承擔責任。
|
||||
|
|
@ -0,0 +1,102 @@
|
|||
歐盟數據庫權利:
|
||||
|
||||
a. 為避免疑義,第 2(a)(1) 條授予您提取、重用、複製及分享數據庫全部或實質部分內容的權利;
|
||||
|
||||
b. 如果您將數據庫的全部或實質部分內容包含在您擁有的數據庫中,則該數據庫的版權必須以與本公共許可協議相同的條件授權;
|
||||
|
||||
c. 您不得對數據庫或其內容施加任何額外或不同的條款或條件,或應用任何有效技術措施,限制根據本公共許可協議授予的權利的行使。
|
||||
|
||||
第 5 節 -- 免責聲明與責任限制。
|
||||
|
||||
a. 除非另有明確說明,授權方不提供任何形式的保證,包括但不限於適銷性、特定用途的適用性、非侵權性、準確性或存在隱藏缺陷的保證。授權方僅在適用法律允許的範圍內提供授權材料。
|
||||
|
||||
b. 在適用法律允許的最大範圍內,授權方不對您因使用授權材料而產生的任何直接、間接、特殊、附帶、後果性或懲罰性損害承擔責任,即使授權方已被告知可能發生此類損害。
|
||||
|
||||
第 6 節 -- 協議期限與終止。
|
||||
|
||||
a. 協議期限。本公共許可協議的期限與授權材料的版權及類似權利的期限一致。儘管有上述規定,如果您未遵守本公共許可協議的條款和條件,則授權方可根據第 6(b) 條終止本公共許可協議。
|
||||
|
||||
b. 終止。
|
||||
|
||||
1. 如果您未遵守本公共許可協議的條款和條件,則您的許可權將自違規行為發生之日起自動終止。
|
||||
|
||||
2. 根據第 6(b)(1) 條終止後,授權方可自行決定恢復本公共許可協議的效力,前提是您糾正了違規行為。
|
||||
|
||||
3. 對於根據本公共許可協議獲得的任何權利,終止不影響在終止前授予的許可權利,前提是這些權利是根據本公共許可協議授予的。第 6(b) 條的條款仍然適用。
|
||||
|
||||
c. 剩餘條款。第 1、5、6、7 節的條款在本公共許可協議終止後仍然有效。
|
||||
|
||||
第 7 節 -- 其他條款。
|
||||
|
||||
a. 如果您分享授權材料或改編材料,授權方可能會要求您移除某些信息(例如歸屬信息),只要在合理範圍內可行。
|
||||
|
||||
b. 本公共許可協議不會減少、限制或限制您根據適用法律享有的任何權利,包括版權例外和限制。
|
||||
|
||||
c. 如果本公共許可協議的任何條款被認定為無效或不可執行,則根據適用法律,該條款應在必要的最低範圍內進行修改或解釋,以使其有效和可執行。如果無法進行此類修改或解釋,則應將該條款視為可分割,且不影響其餘條款的有效性和可執行性。
|
||||
|
||||
d. 無論是否另有約定,授權方不得根據本公共許可協議授予任何默示許可。
|
||||
|
||||
e. 適用法律和管轄權。本公共許可協議的條款和條件應受您居住地法律的管轄,除非適用法律另有規定。
|
||||
|
||||
=======================================================================
|
||||
|
||||
Creative Commons Corporation ("Creative Commons") 並非律師事務所,亦不提供法律服務或法律建議。Creative Commons 公共許可協議的分發不會創建律師與客戶或其他關係。Creative Commons 僅以“現狀”基礎提供其許可協議及相關信息,並不對其許可協議、根據其條款和條件授權的任何材料或相關信息作出任何保證。Creative Commons 在法律允許的最大範圍內,對因使用其許可協議而導致的任何損害不承擔責任。
|
||||
權利,然後您擁有原創資料庫權利的資料庫(但不包括其個別內容)屬於改編材料,
|
||||
|
||||
包括為了第 3(b) 節的目的;以及
|
||||
c. 如果您分享資料庫的全部或大部分內容,您必須遵守第 3(a) 節中的條件。
|
||||
|
||||
為免生疑,本第 4 節是補充而非取代您在本公共許可下的義務,當授權權利包括其他版權及類似權利時。
|
||||
|
||||
|
||||
第 5 節 —— 免責聲明及責任限制。
|
||||
|
||||
a. 除非授權方另有單獨承諾,在可能的範圍內,授權方以「現狀」及「可用」的方式提供授權材料,並且不對授權材料作出任何形式的陳述或保證,包括明示、默示、法定或其他形式的保證。這包括但不限於所有權保證、適銷性、特定用途的適用性、非侵權、無隱性或其他缺陷、準確性或是否存在錯誤(無論是否已知或可發現)。如果法律不允許完全或部分免除保證,則此免責聲明可能不適用於您。
|
||||
|
||||
b. 在可能的範圍內,授權方在任何法律理論(包括但不限於疏忽)或其他情況下,均不對您因本公共許可或使用授權材料而導致的任何直接、特殊、間接、附帶、後果性、懲罰性、示範性或其他損失、成本、費用或損害承擔責任,即使授權方已被告知可能發生此類損失、成本、費用或損害。如果法律不允許完全或部分限制責任,則此責任限制可能不適用於您。
|
||||
|
||||
c. 上述免責聲明及責任限制應以最接近絕對免責及放棄所有責任的方式解釋,在可能的範圍內。
|
||||
|
||||
|
||||
第 6 節 —— 有效期及終止。
|
||||
|
||||
a. 本公共許可適用於授權版權及類似權利的有效期。然而,如果您未能遵守本公共許可,則您在本公共許可下的權利將自動終止。
|
||||
|
||||
b. 如果您根據第 6(a) 節失去使用授權材料的權利,該權利可恢復:
|
||||
|
||||
1. 自您發現違規之日起 30 天內糾正違規行為,則自糾正之日起自動恢復;或
|
||||
2. 經授權方明確恢復。
|
||||
|
||||
為免生疑,本第 6(b) 節不影響授權方因您違反本公共許可而尋求補救的任何權利。
|
||||
|
||||
c. 為免生疑,授權方亦可隨時以其他條款或條件提供授權材料或停止分發授權材料;然而,這不會終止本公共許可。
|
||||
|
||||
d. 第 1 節、第 5 節、第 6 節、第 7 節及第 8 節在本公共許可終止後仍然有效。
|
||||
|
||||
|
||||
第 7 節 —— 其他條款及條件。
|
||||
|
||||
a. 除非授權方明確同意,授權方不受您傳達的任何額外或不同條款及條件的約束。
|
||||
|
||||
b. 關於授權材料的任何安排、理解或協議,若未在此明確說明,則與本公共許可的條款及條件分開且獨立。
|
||||
|
||||
|
||||
第 8 節 —— 解釋。
|
||||
|
||||
a. 為免生疑,本公共許可不會且不應被解釋為減少、限制、約束或對任何在未經本公共許可授權的情況下合法使用授權材料的行為施加條件。
|
||||
|
||||
b. 在可能的範圍內,如果本公共許可的任何條款被認為不可執行,則該條款應自動調整至最低必要程度以使其可執行。如果該條款無法調整,則應從本公共許可中刪除,而不影響其餘條款及條件的可執行性。
|
||||
|
||||
c. 除非授權方明確同意,否則本公共許可的任何條款或條件均不會被放棄,且任何未遵守的行為均不會被視為已獲得同意。
|
||||
|
||||
d. 本公共許可中的任何內容均不構成或可被解釋為限制或放棄授權方或您所享有的任何特權及豁免,包括免於任何司法管轄或權力的法律程序。
|
||||
|
||||
|
||||
=======================================================================
|
||||
|
||||
Creative Commons 並非其公共許可的當事方。然而,Creative Commons 可選擇將其公共許可應用於其發布的材料,在此情況下將被視為「授權方」。Creative Commons 公共許可的文本已根據 CC0 公共領域貢獻被奉獻於公共領域。除非為了有限目的表明材料是根據 Creative Commons 公共許可共享或根據 Creative Commons 政策(發布於 creativecommons.org/policies)另行允許,Creative Commons 不授權使用「Creative Commons」商標或任何其他 Creative Commons 的商標或標誌,除非事先書面同意,包括但不限於任何未經授權修改其公共許可或任何其他安排、理解或協議涉及授權材料的使用。為免生疑,本段不構成本公共許可的一部分。
|
||||
|
||||
Creative Commons 可通過 creativecommons.org 聯繫。
|
||||
|
||||
**免責聲明**:
|
||||
本文件已使用人工智能翻譯服務 [Co-op Translator](https://github.com/Azure/co-op-translator) 進行翻譯。儘管我們致力於提供準確的翻譯,但請注意,自動翻譯可能包含錯誤或不準確之處。原始文件的母語版本應被視為權威來源。對於重要信息,建議使用專業人工翻譯。我們對因使用此翻譯而引起的任何誤解或錯誤解釋概不負責。
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
所有課程的手繪筆記可以在這裡下載。
|
||||
|
||||
🎨 製作人:Tomomi Imura(Twitter: [@girlie_mac](https://twitter.com/girlie_mac),GitHub: [girliemac](https://github.com/girliemac))
|
||||
|
||||
[](https://creativecommons.org/licenses/by-sa/4.0/)
|
||||
|
||||
**免責聲明**:
|
||||
本文件已使用人工智能翻譯服務 [Co-op Translator](https://github.com/Azure/co-op-translator) 進行翻譯。雖然我們致力於提供準確的翻譯,但請注意,自動翻譯可能包含錯誤或不準確之處。原始語言的文件應被視為具權威性的來源。對於重要資訊,建議使用專業人工翻譯。我們對因使用此翻譯而引起的任何誤解或錯誤解釋概不負責。
|
||||
|
|
@ -0,0 +1,278 @@
|
|||
# AI-For-Beginners 故障排除指南
|
||||
|
||||
此指南旨在幫助您解決使用或貢獻 [AI-For-Beginners](https://github.com/microsoft/AI-For-Beginners) 資源庫時遇到的常見問題。每個問題都包含背景、症狀、解釋以及逐步解決方案。
|
||||
|
||||
---
|
||||
|
||||
## 目錄
|
||||
|
||||
- [一般問題](../..)
|
||||
- [安裝問題](../..)
|
||||
- [配置問題](../..)
|
||||
- [運行筆記本](../..)
|
||||
- [性能問題](../..)
|
||||
- [教科書網站問題](../..)
|
||||
- [貢獻問題](../..)
|
||||
- [常見問題](../..)
|
||||
- [尋求幫助](../..)
|
||||
|
||||
---
|
||||
|
||||
## 一般問題
|
||||
|
||||
### 1. 資源庫無法正確克隆
|
||||
|
||||
**背景:** 克隆操作允許您將資源庫複製到您的機器上。
|
||||
|
||||
**症狀:**
|
||||
- 錯誤:`fatal: repository not found`
|
||||
- 錯誤:`Permission denied (publickey)`
|
||||
|
||||
**可能原因:**
|
||||
- 資源庫 URL 不正確
|
||||
- 權限不足
|
||||
- SSH 密鑰未配置
|
||||
|
||||
**解決方案:**
|
||||
1. **檢查資源庫 URL。**
|
||||
使用 HTTPS URL:
|
||||
```
|
||||
git clone https://github.com/microsoft/AI-For-Beginners.git
|
||||
```
|
||||
2. **如果 SSH 失敗,切換到 HTTPS。**
|
||||
如果您看到 `Permission denied (publickey)`,請使用上述 HTTPS 連結代替 SSH。
|
||||
3. **配置 SSH 密鑰(可選)。**
|
||||
如果您希望使用 SSH,請參考 [GitHub 的 SSH 指南](https://docs.github.com/en/authentication/connecting-to-github-with-ssh)。
|
||||
|
||||
---
|
||||
|
||||
## 安裝問題
|
||||
|
||||
### 2. Python 環境問題
|
||||
|
||||
**背景:** 此資源庫依賴於 Python 和多個庫。
|
||||
|
||||
**症狀:**
|
||||
- 錯誤:`ModuleNotFoundError: No module named '<package>'`
|
||||
- 運行腳本或筆記本時出現導入錯誤
|
||||
|
||||
**可能原因:**
|
||||
- 未安裝依賴項
|
||||
- Python 版本不正確
|
||||
|
||||
**解決方案:**
|
||||
1. **設置虛擬環境。**
|
||||
```bash
|
||||
python -m venv venv
|
||||
source venv/bin/activate # On Windows: venv\Scripts\activate
|
||||
```
|
||||
2. **安裝依賴項。**
|
||||
```bash
|
||||
pip install -r requirements.txt
|
||||
```
|
||||
3. **檢查 Python 版本。**
|
||||
使用 Python 3.7 或更新版本。
|
||||
```bash
|
||||
python --version
|
||||
```
|
||||
|
||||
### 3. 未安裝 Jupyter
|
||||
|
||||
**背景:** 筆記本是核心學習資源。
|
||||
|
||||
**症狀:**
|
||||
- 錯誤:`jupyter: command not found`
|
||||
- 筆記本無法啟動
|
||||
|
||||
**可能原因:**
|
||||
- 未安裝 Jupyter
|
||||
|
||||
**解決方案:**
|
||||
1. **安裝 Jupyter Notebook。**
|
||||
```bash
|
||||
pip install notebook
|
||||
```
|
||||
或者,如果使用 Anaconda:
|
||||
```bash
|
||||
conda install notebook
|
||||
```
|
||||
2. **啟動 Jupyter Notebook。**
|
||||
```bash
|
||||
jupyter notebook
|
||||
```
|
||||
|
||||
### 4. 依賴版本衝突
|
||||
|
||||
**背景:** 如果包版本不匹配,項目可能會出現問題。
|
||||
|
||||
**症狀:**
|
||||
- 關於版本不兼容的錯誤或警告
|
||||
|
||||
**可能原因:**
|
||||
- 舊的或衝突的 Python 包
|
||||
|
||||
**解決方案:**
|
||||
1. **在乾淨的環境中安裝。**
|
||||
刪除舊的 venv/conda 環境並創建新環境。
|
||||
2. **使用精確版本。**
|
||||
始終運行:
|
||||
```bash
|
||||
pip install -r requirements.txt
|
||||
```
|
||||
如果此操作失敗,請按照 README 中的說明手動安裝缺失的包。
|
||||
|
||||
---
|
||||
|
||||
## 配置問題
|
||||
|
||||
### 5. 環境變量未設置
|
||||
|
||||
**背景:** 某些模塊可能需要密鑰、令牌或配置設置。
|
||||
|
||||
**症狀:**
|
||||
- 錯誤:`KeyError` 或關於缺少配置的警告
|
||||
|
||||
**可能原因:**
|
||||
- 未設置所需的環境變量
|
||||
|
||||
**解決方案:**
|
||||
1. **檢查 `.env.example` 或類似文件。**
|
||||
2. **創建 `.env` 文件並填寫所需值。**
|
||||
3. **設置環境變量後重新加載終端或 IDE。**
|
||||
|
||||
---
|
||||
|
||||
## 運行筆記本
|
||||
|
||||
### 6. 筆記本無法打開或運行
|
||||
|
||||
**背景:** Jupyter 筆記本需要正確的設置。
|
||||
|
||||
**症狀:**
|
||||
- 筆記本無法啟動
|
||||
- 瀏覽器未自動打開
|
||||
|
||||
**可能原因:**
|
||||
- 未安裝 Jupyter
|
||||
- 瀏覽器配置問題
|
||||
|
||||
**解決方案:**
|
||||
1. **安裝 Jupyter(參見上方的安裝問題)。**
|
||||
2. **手動打開筆記本。**
|
||||
- 從終端複製 URL(例如,`http://localhost:8888/?token=...`),並將其粘貼到瀏覽器中。
|
||||
|
||||
### 7. 核心崩潰或卡住
|
||||
|
||||
**背景:** 筆記本核心可能因資源限制或代碼錯誤而崩潰。
|
||||
|
||||
**症狀:**
|
||||
- 核心反覆死機或重啟
|
||||
- 內存不足錯誤
|
||||
|
||||
**可能原因:**
|
||||
- 大型數據集
|
||||
- 不兼容的代碼或包
|
||||
|
||||
**解決方案:**
|
||||
1. **重啟核心。**
|
||||
使用 Jupyter 中的“重啟核心”按鈕。
|
||||
2. **檢查內存使用情況。**
|
||||
關閉未使用的應用程序。
|
||||
3. **在雲平台上運行筆記本。**
|
||||
使用 [Google Colab](https://colab.research.google.com/) 或 [Azure Notebooks](https://notebooks.azure.com/)。
|
||||
|
||||
---
|
||||
|
||||
## 性能問題
|
||||
|
||||
### 8. 筆記本運行緩慢
|
||||
|
||||
**背景:** 某些 AI 任務需要大量內存和 CPU。
|
||||
|
||||
**症狀:**
|
||||
- 運行速度慢
|
||||
- 筆記本電腦風扇聲音很大
|
||||
|
||||
**可能原因:**
|
||||
- 大型數據集或模型
|
||||
- 系統資源有限
|
||||
|
||||
**解決方案:**
|
||||
1. **使用雲平台。**
|
||||
- 將筆記本上傳到 Colab 或 Azure Notebooks。
|
||||
2. **減少數據集大小。**
|
||||
- 使用示例數據進行練習。
|
||||
3. **關閉不必要的程序。**
|
||||
- 釋放系統 RAM。
|
||||
|
||||
---
|
||||
|
||||
## 教科書網站問題
|
||||
|
||||
### 9. 章節無法加載
|
||||
|
||||
**背景:** 在線教科書顯示課程和章節。
|
||||
|
||||
**症狀:**
|
||||
- 某章節(例如 Transformers/BERT)丟失或無法打開
|
||||
|
||||
**已知問題:**
|
||||
- [問題 #303](https://github.com/microsoft/AI-For-Beginners/issues/303):“18 Transformers. BERT. 無法在教科書網站上打開。” 由文件名錯誤引起(`READMEtransformers.md` 而非 `README.md`)。
|
||||
|
||||
**解決方案:**
|
||||
1. **檢查文件重命名錯誤。**
|
||||
如果您是貢獻者,請確保章節文件命名為 `README.md`。
|
||||
2. **報告缺失文件。**
|
||||
在 GitHub 上提交問題,提供章節名稱和錯誤詳情。
|
||||
|
||||
---
|
||||
|
||||
## 貢獻問題
|
||||
|
||||
### 10. PR 未被接受或構建失敗
|
||||
|
||||
**背景:** 貢獻必須通過測試並遵循指南。
|
||||
|
||||
**症狀:**
|
||||
- Pull request 被拒絕
|
||||
- CI/CD 管道錯誤
|
||||
|
||||
**可能原因:**
|
||||
- 測試失敗
|
||||
- 未遵循編碼標準
|
||||
|
||||
**解決方案:**
|
||||
1. **閱讀貢獻指南。**
|
||||
- 遵循資源庫的 [CONTRIBUTING.md](https://github.com/microsoft/AI-For-Beginners/blob/main/CONTRIBUTING.md)。
|
||||
2. **在推送之前本地運行測試。**
|
||||
3. **檢查格式要求或 linting 規則。**
|
||||
|
||||
---
|
||||
|
||||
## 常見問題
|
||||
|
||||
### 我在哪裡可以找到特定模塊的幫助?
|
||||
- 每個模塊通常都有自己的 README。從那裡開始了解設置和使用提示。
|
||||
|
||||
### 如何報告錯誤或請求功能?
|
||||
- [提交 GitHub 問題](https://github.com/microsoft/AI-For-Beginners/issues/new),提供清晰的描述和重現步驟。
|
||||
|
||||
### 如果我的問題未列出,我可以尋求幫助嗎?
|
||||
- 可以!先搜索現有問題,如果找不到您的問題,請創建新問題。
|
||||
|
||||
---
|
||||
|
||||
## 尋求幫助
|
||||
|
||||
- **檢查問題:** [GitHub 問題](https://github.com/microsoft/AI-For-Beginners/issues)
|
||||
- **提出問題:** 使用 GitHub 討論或提交問題。
|
||||
- **社群:** 查看資源庫鏈接以獲取聊天/論壇選項。
|
||||
|
||||
---
|
||||
|
||||
_最後更新日期:2025-09-20_
|
||||
|
||||
---
|
||||
|
||||
**免責聲明**:
|
||||
本文件已使用人工智能翻譯服務 [Co-op Translator](https://github.com/Azure/co-op-translator) 進行翻譯。雖然我們致力於提供準確的翻譯,但請注意,自動翻譯可能包含錯誤或不準確之處。原始文件的母語版本應被視為權威來源。對於重要信息,建議使用專業人工翻譯。我們對因使用此翻譯而引起的任何誤解或錯誤解釋概不負責。
|
||||
|
|
@ -0,0 +1,398 @@
|
|||
{
|
||||
"AGENTS.md": {
|
||||
"original_hash": "6b11a37115944252ab3ed04e358d830d",
|
||||
"translation_date": "2025-10-03T09:15:17+00:00",
|
||||
"source_file": "AGENTS.md",
|
||||
"language_code": "zh-TW"
|
||||
},
|
||||
"README.md": {
|
||||
"original_hash": "1984fc89dd304a8a33ab5584691a99aa",
|
||||
"translation_date": "2026-01-30T01:22:41+00:00",
|
||||
"source_file": "README.md",
|
||||
"language_code": "zh-TW"
|
||||
},
|
||||
"SECURITY.md": {
|
||||
"original_hash": "a583f49d359c7ebba61433e4dfcd05a9",
|
||||
"translation_date": "2025-08-24T21:42:03+00:00",
|
||||
"source_file": "SECURITY.md",
|
||||
"language_code": "zh-TW"
|
||||
},
|
||||
"etc/CODE_OF_CONDUCT.md": {
|
||||
"original_hash": "c06b12caf3c901eb3156e3dd5b0aea56",
|
||||
"translation_date": "2025-08-24T22:17:50+00:00",
|
||||
"source_file": "etc/CODE_OF_CONDUCT.md",
|
||||
"language_code": "zh-TW"
|
||||
},
|
||||
"etc/CONTRIBUTING.md": {
|
||||
"original_hash": "847a587aa1b83f4d00858183ff3ed18a",
|
||||
"translation_date": "2025-08-24T22:18:21+00:00",
|
||||
"source_file": "etc/CONTRIBUTING.md",
|
||||
"language_code": "zh-TW"
|
||||
},
|
||||
"etc/Mindmap.md": {
|
||||
"original_hash": "f2f88dbd2debd38e26149b27b1fd272d",
|
||||
"translation_date": "2025-08-24T22:18:38+00:00",
|
||||
"source_file": "etc/Mindmap.md",
|
||||
"language_code": "zh-TW"
|
||||
},
|
||||
"etc/SUPPORT.md": {
|
||||
"original_hash": "fdfc08baee91e402938a2b1f94fe0949",
|
||||
"translation_date": "2025-08-24T22:17:40+00:00",
|
||||
"source_file": "etc/SUPPORT.md",
|
||||
"language_code": "zh-TW"
|
||||
},
|
||||
"etc/TRANSLATIONS.md": {
|
||||
"original_hash": "62b3e3ad5182edb905eec649a87eeeb4",
|
||||
"translation_date": "2025-08-24T22:18:01+00:00",
|
||||
"source_file": "etc/TRANSLATIONS.md",
|
||||
"language_code": "zh-TW"
|
||||
},
|
||||
"etc/quiz-app/README.md": {
|
||||
"original_hash": "d699cf8509f74baa5b0b838de5cf0662",
|
||||
"translation_date": "2025-08-24T22:19:26+00:00",
|
||||
"source_file": "etc/quiz-app/README.md",
|
||||
"language_code": "zh-TW"
|
||||
},
|
||||
"examples/README.md": {
|
||||
"original_hash": "0d1babfdcbeb46525f2db3fbaaa54cd7",
|
||||
"translation_date": "2025-10-03T11:28:01+00:00",
|
||||
"source_file": "examples/README.md",
|
||||
"language_code": "zh-TW"
|
||||
},
|
||||
"lessons/0-course-setup/for-teachers.md": {
|
||||
"original_hash": "a094ef9927883de1cfcee51dbd143381",
|
||||
"translation_date": "2025-08-24T22:16:55+00:00",
|
||||
"source_file": "lessons/0-course-setup/for-teachers.md",
|
||||
"language_code": "zh-TW"
|
||||
},
|
||||
"lessons/0-course-setup/how-to-run.md": {
|
||||
"original_hash": "a4717bd9103b9f6cd84d534b83534689",
|
||||
"translation_date": "2026-01-15T12:04:06+00:00",
|
||||
"source_file": "lessons/0-course-setup/how-to-run.md",
|
||||
"language_code": "zh-TW"
|
||||
},
|
||||
"lessons/0-course-setup/setup.md": {
|
||||
"original_hash": "7b4e5b8956915870d0a0ed3cc5890042",
|
||||
"translation_date": "2025-12-12T19:23:10+00:00",
|
||||
"source_file": "lessons/0-course-setup/setup.md",
|
||||
"language_code": "zh-TW"
|
||||
},
|
||||
"lessons/1-Intro/README.md": {
|
||||
"original_hash": "f57e8aa46141fd220b16ffed8f11aec7",
|
||||
"translation_date": "2025-11-18T20:54:16+00:00",
|
||||
"source_file": "lessons/1-Intro/README.md",
|
||||
"language_code": "zh-TW"
|
||||
},
|
||||
"lessons/1-Intro/assignment.md": {
|
||||
"original_hash": "a334df77a82aaaf2a29c77065d3e481e",
|
||||
"translation_date": "2025-11-18T20:55:23+00:00",
|
||||
"source_file": "lessons/1-Intro/assignment.md",
|
||||
"language_code": "zh-TW"
|
||||
},
|
||||
"lessons/2-Symbolic/README.md": {
|
||||
"original_hash": "f9f06b266b8b2bfc6b8792ff2bb1bea4",
|
||||
"translation_date": "2026-01-15T12:05:25+00:00",
|
||||
"source_file": "lessons/2-Symbolic/README.md",
|
||||
"language_code": "zh-TW"
|
||||
},
|
||||
"lessons/2-Symbolic/assignment.md": {
|
||||
"original_hash": "a057a8604f3976c3e309884453f1fad0",
|
||||
"translation_date": "2025-08-24T22:16:12+00:00",
|
||||
"source_file": "lessons/2-Symbolic/assignment.md",
|
||||
"language_code": "zh-TW"
|
||||
},
|
||||
"lessons/3-NeuralNetworks/03-Perceptron/README.md": {
|
||||
"original_hash": "c34cbba802058b6fa267e1a294d4e510",
|
||||
"translation_date": "2025-09-23T13:01:27+00:00",
|
||||
"source_file": "lessons/3-NeuralNetworks/03-Perceptron/README.md",
|
||||
"language_code": "zh-TW"
|
||||
},
|
||||
"lessons/3-NeuralNetworks/03-Perceptron/lab/README.md": {
|
||||
"original_hash": "ba5d1eb353d20d3e7181066b3c424b99",
|
||||
"translation_date": "2025-08-31T09:18:09+00:00",
|
||||
"source_file": "lessons/3-NeuralNetworks/03-Perceptron/lab/README.md",
|
||||
"language_code": "zh-TW"
|
||||
},
|
||||
"lessons/3-NeuralNetworks/04-OwnFramework/README.md": {
|
||||
"original_hash": "789d6c3fb6fc7948a470b33078a5983a",
|
||||
"translation_date": "2025-09-23T13:01:06+00:00",
|
||||
"source_file": "lessons/3-NeuralNetworks/04-OwnFramework/README.md",
|
||||
"language_code": "zh-TW"
|
||||
},
|
||||
"lessons/3-NeuralNetworks/04-OwnFramework/lab/README.md": {
|
||||
"original_hash": "48fdd704d483e19bc3d7464074c9fcbe",
|
||||
"translation_date": "2025-08-24T22:08:43+00:00",
|
||||
"source_file": "lessons/3-NeuralNetworks/04-OwnFramework/lab/README.md",
|
||||
"language_code": "zh-TW"
|
||||
},
|
||||
"lessons/3-NeuralNetworks/05-Frameworks/README.md": {
|
||||
"original_hash": "ddd216f558a255260a9374008002c971",
|
||||
"translation_date": "2025-09-23T13:01:46+00:00",
|
||||
"source_file": "lessons/3-NeuralNetworks/05-Frameworks/README.md",
|
||||
"language_code": "zh-TW"
|
||||
},
|
||||
"lessons/3-NeuralNetworks/05-Frameworks/lab/README.md": {
|
||||
"original_hash": "e452d897efb9a89700f41021834cf6e5",
|
||||
"translation_date": "2025-08-24T22:09:56+00:00",
|
||||
"source_file": "lessons/3-NeuralNetworks/05-Frameworks/lab/README.md",
|
||||
"language_code": "zh-TW"
|
||||
},
|
||||
"lessons/3-NeuralNetworks/README.md": {
|
||||
"original_hash": "f862a99d88088163df12270e2f2ad6c3",
|
||||
"translation_date": "2025-10-03T12:43:43+00:00",
|
||||
"source_file": "lessons/3-NeuralNetworks/README.md",
|
||||
"language_code": "zh-TW"
|
||||
},
|
||||
"lessons/4-ComputerVision/06-IntroCV/README.md": {
|
||||
"original_hash": "feeca98225cb420afc89415f24f63d92",
|
||||
"translation_date": "2025-09-23T12:57:14+00:00",
|
||||
"source_file": "lessons/4-ComputerVision/06-IntroCV/README.md",
|
||||
"language_code": "zh-TW"
|
||||
},
|
||||
"lessons/4-ComputerVision/06-IntroCV/lab/README.md": {
|
||||
"original_hash": "3d53d6409f80970f7281a45dee35328a",
|
||||
"translation_date": "2025-08-24T22:01:18+00:00",
|
||||
"source_file": "lessons/4-ComputerVision/06-IntroCV/lab/README.md",
|
||||
"language_code": "zh-TW"
|
||||
},
|
||||
"lessons/4-ComputerVision/07-ConvNets/CNN_Architectures.md": {
|
||||
"original_hash": "53faab85adfcebd8c10bcd71dc2fa557",
|
||||
"translation_date": "2025-09-23T12:56:39+00:00",
|
||||
"source_file": "lessons/4-ComputerVision/07-ConvNets/CNN_Architectures.md",
|
||||
"language_code": "zh-TW"
|
||||
},
|
||||
"lessons/4-ComputerVision/07-ConvNets/README.md": {
|
||||
"original_hash": "a560d5b845962cf33dc102266e409568",
|
||||
"translation_date": "2025-09-23T12:56:22+00:00",
|
||||
"source_file": "lessons/4-ComputerVision/07-ConvNets/README.md",
|
||||
"language_code": "zh-TW"
|
||||
},
|
||||
"lessons/4-ComputerVision/07-ConvNets/lab/README.md": {
|
||||
"original_hash": "b70fcf7fcee862990f848c679090943f",
|
||||
"translation_date": "2025-10-03T14:53:05+00:00",
|
||||
"source_file": "lessons/4-ComputerVision/07-ConvNets/lab/README.md",
|
||||
"language_code": "zh-TW"
|
||||
},
|
||||
"lessons/4-ComputerVision/08-TransferLearning/README.md": {
|
||||
"original_hash": "178c0b5ee5395733eb18aec51e71a0a9",
|
||||
"translation_date": "2025-09-23T12:56:56+00:00",
|
||||
"source_file": "lessons/4-ComputerVision/08-TransferLearning/README.md",
|
||||
"language_code": "zh-TW"
|
||||
},
|
||||
"lessons/4-ComputerVision/08-TransferLearning/TrainingTricks.md": {
|
||||
"original_hash": "ae074cd940fc2f4dc24fc07b66ccbd99",
|
||||
"translation_date": "2025-08-24T22:02:18+00:00",
|
||||
"source_file": "lessons/4-ComputerVision/08-TransferLearning/TrainingTricks.md",
|
||||
"language_code": "zh-TW"
|
||||
},
|
||||
"lessons/4-ComputerVision/08-TransferLearning/lab/README.md": {
|
||||
"original_hash": "7765935c35fcee69b9fe2d0cfd6963e2",
|
||||
"translation_date": "2025-08-24T22:03:29+00:00",
|
||||
"source_file": "lessons/4-ComputerVision/08-TransferLearning/lab/README.md",
|
||||
"language_code": "zh-TW"
|
||||
},
|
||||
"lessons/4-ComputerVision/09-Autoencoders/README.md": {
|
||||
"original_hash": "1b8d9e1b3a6f1daa864b1ff3dfc3076d",
|
||||
"translation_date": "2025-09-23T12:58:19+00:00",
|
||||
"source_file": "lessons/4-ComputerVision/09-Autoencoders/README.md",
|
||||
"language_code": "zh-TW"
|
||||
},
|
||||
"lessons/4-ComputerVision/10-GANs/README.md": {
|
||||
"original_hash": "0ff65b4da07b23697235de2beb2a3c25",
|
||||
"translation_date": "2025-09-23T12:59:02+00:00",
|
||||
"source_file": "lessons/4-ComputerVision/10-GANs/README.md",
|
||||
"language_code": "zh-TW"
|
||||
},
|
||||
"lessons/4-ComputerVision/11-ObjectDetection/README.md": {
|
||||
"original_hash": "d76a7eda28de5210c8b1ba50a6216c69",
|
||||
"translation_date": "2025-09-23T12:57:48+00:00",
|
||||
"source_file": "lessons/4-ComputerVision/11-ObjectDetection/README.md",
|
||||
"language_code": "zh-TW"
|
||||
},
|
||||
"lessons/4-ComputerVision/11-ObjectDetection/lab/README.md": {
|
||||
"original_hash": "ad568d55ae65c856fe929fc2b278510a",
|
||||
"translation_date": "2025-08-24T21:58:40+00:00",
|
||||
"source_file": "lessons/4-ComputerVision/11-ObjectDetection/lab/README.md",
|
||||
"language_code": "zh-TW"
|
||||
},
|
||||
"lessons/4-ComputerVision/12-Segmentation/README.md": {
|
||||
"original_hash": "6568aaae7e0e4afed4b5d74b5b223700",
|
||||
"translation_date": "2025-09-23T12:58:46+00:00",
|
||||
"source_file": "lessons/4-ComputerVision/12-Segmentation/README.md",
|
||||
"language_code": "zh-TW"
|
||||
},
|
||||
"lessons/4-ComputerVision/12-Segmentation/lab/README.md": {
|
||||
"original_hash": "365f0decfe0f47b460bbde8227c5009d",
|
||||
"translation_date": "2025-08-24T21:56:34+00:00",
|
||||
"source_file": "lessons/4-ComputerVision/12-Segmentation/lab/README.md",
|
||||
"language_code": "zh-TW"
|
||||
},
|
||||
"lessons/4-ComputerVision/README.md": {
|
||||
"original_hash": "58a52f000089c1d8906a4daa4ab1169b",
|
||||
"translation_date": "2025-08-24T21:55:02+00:00",
|
||||
"source_file": "lessons/4-ComputerVision/README.md",
|
||||
"language_code": "zh-TW"
|
||||
},
|
||||
"lessons/5-NLP/13-TextRep/README.md": {
|
||||
"original_hash": "dbd3f73e4139f030ecb2e20387d70fee",
|
||||
"translation_date": "2025-09-23T13:04:31+00:00",
|
||||
"source_file": "lessons/5-NLP/13-TextRep/README.md",
|
||||
"language_code": "zh-TW"
|
||||
},
|
||||
"lessons/5-NLP/13-TextRep/assignment.md": {
|
||||
"original_hash": "cdc1f2e631f055f3473b36d18e4760b3",
|
||||
"translation_date": "2025-08-24T21:47:04+00:00",
|
||||
"source_file": "lessons/5-NLP/13-TextRep/assignment.md",
|
||||
"language_code": "zh-TW"
|
||||
},
|
||||
"lessons/5-NLP/14-Embeddings/README.md": {
|
||||
"original_hash": "b708c9b85b833864c73c6281f1e6b96e",
|
||||
"translation_date": "2025-09-23T13:04:15+00:00",
|
||||
"source_file": "lessons/5-NLP/14-Embeddings/README.md",
|
||||
"language_code": "zh-TW"
|
||||
},
|
||||
"lessons/5-NLP/14-Embeddings/assignment.md": {
|
||||
"original_hash": "bc690ecf68b38d311cc9e12f3144a28c",
|
||||
"translation_date": "2025-08-24T21:45:17+00:00",
|
||||
"source_file": "lessons/5-NLP/14-Embeddings/assignment.md",
|
||||
"language_code": "zh-TW"
|
||||
},
|
||||
"lessons/5-NLP/15-LanguageModeling/README.md": {
|
||||
"original_hash": "7ba20f54a5bfcd6521018cdfb17c7c57",
|
||||
"translation_date": "2025-09-23T13:02:37+00:00",
|
||||
"source_file": "lessons/5-NLP/15-LanguageModeling/README.md",
|
||||
"language_code": "zh-TW"
|
||||
},
|
||||
"lessons/5-NLP/15-LanguageModeling/lab/README.md": {
|
||||
"original_hash": "5130f01fdc5ebb83032b23d489027aac",
|
||||
"translation_date": "2025-08-24T21:47:39+00:00",
|
||||
"source_file": "lessons/5-NLP/15-LanguageModeling/lab/README.md",
|
||||
"language_code": "zh-TW"
|
||||
},
|
||||
"lessons/5-NLP/16-RNN/README.md": {
|
||||
"original_hash": "e2273cc150380a5e191903cea858f021",
|
||||
"translation_date": "2025-09-23T13:03:50+00:00",
|
||||
"source_file": "lessons/5-NLP/16-RNN/README.md",
|
||||
"language_code": "zh-TW"
|
||||
},
|
||||
"lessons/5-NLP/16-RNN/assignment.md": {
|
||||
"original_hash": "47f7d3c6a5373543e051e4d1140ce898",
|
||||
"translation_date": "2025-08-24T21:44:31+00:00",
|
||||
"source_file": "lessons/5-NLP/16-RNN/assignment.md",
|
||||
"language_code": "zh-TW"
|
||||
},
|
||||
"lessons/5-NLP/17-GenerativeNetworks/README.md": {
|
||||
"original_hash": "51be6057374d01d70e07dd5ec88ebc0d",
|
||||
"translation_date": "2025-09-23T13:02:22+00:00",
|
||||
"source_file": "lessons/5-NLP/17-GenerativeNetworks/README.md",
|
||||
"language_code": "zh-TW"
|
||||
},
|
||||
"lessons/5-NLP/17-GenerativeNetworks/lab/README.md": {
|
||||
"original_hash": "439e12796197a90e7623d4c9c057b9c2",
|
||||
"translation_date": "2025-08-24T21:46:12+00:00",
|
||||
"source_file": "lessons/5-NLP/17-GenerativeNetworks/lab/README.md",
|
||||
"language_code": "zh-TW"
|
||||
},
|
||||
"lessons/5-NLP/18-Transformers/README.md": {
|
||||
"original_hash": "f335dfcb4a993920504c387973a36957",
|
||||
"translation_date": "2025-09-23T13:02:48+00:00",
|
||||
"source_file": "lessons/5-NLP/18-Transformers/README.md",
|
||||
"language_code": "zh-TW"
|
||||
},
|
||||
"lessons/5-NLP/18-Transformers/assignment.md": {
|
||||
"original_hash": "177f3ea3995d725e6f9f5c66af16edcd",
|
||||
"translation_date": "2025-08-24T21:50:08+00:00",
|
||||
"source_file": "lessons/5-NLP/18-Transformers/assignment.md",
|
||||
"language_code": "zh-TW"
|
||||
},
|
||||
"lessons/5-NLP/19-NER/README.md": {
|
||||
"original_hash": "6522312ff835796ca34136a9462fafb2",
|
||||
"translation_date": "2025-09-23T13:03:32+00:00",
|
||||
"source_file": "lessons/5-NLP/19-NER/README.md",
|
||||
"language_code": "zh-TW"
|
||||
},
|
||||
"lessons/5-NLP/19-NER/lab/README.md": {
|
||||
"original_hash": "032bda5068f543d6c1fcb30c34231461",
|
||||
"translation_date": "2025-08-24T21:52:25+00:00",
|
||||
"source_file": "lessons/5-NLP/19-NER/lab/README.md",
|
||||
"language_code": "zh-TW"
|
||||
},
|
||||
"lessons/5-NLP/20-LangModels/README.md": {
|
||||
"original_hash": "97836d30a6bec736f8e3b4411c572bc2",
|
||||
"translation_date": "2025-09-23T13:03:16+00:00",
|
||||
"source_file": "lessons/5-NLP/20-LangModels/README.md",
|
||||
"language_code": "zh-TW"
|
||||
},
|
||||
"lessons/5-NLP/README.md": {
|
||||
"original_hash": "8ef02a9318257ea140ed3ed74442096d",
|
||||
"translation_date": "2025-08-24T21:43:04+00:00",
|
||||
"source_file": "lessons/5-NLP/README.md",
|
||||
"language_code": "zh-TW"
|
||||
},
|
||||
"lessons/6-Other/21-GeneticAlgorithms/README.md": {
|
||||
"original_hash": "6bbd632dfe6c62e5f66bb51fd78c174a",
|
||||
"translation_date": "2025-09-23T12:55:01+00:00",
|
||||
"source_file": "lessons/6-Other/21-GeneticAlgorithms/README.md",
|
||||
"language_code": "zh-TW"
|
||||
},
|
||||
"lessons/6-Other/22-DeepRL/README.md": {
|
||||
"original_hash": "04395657fc01648f8f70484d0e55ab67",
|
||||
"translation_date": "2025-09-23T12:55:59+00:00",
|
||||
"source_file": "lessons/6-Other/22-DeepRL/README.md",
|
||||
"language_code": "zh-TW"
|
||||
},
|
||||
"lessons/6-Other/22-DeepRL/lab/README.md": {
|
||||
"original_hash": "7bd8dc72040e98e35e7225e34058cd4e",
|
||||
"translation_date": "2025-08-24T22:06:36+00:00",
|
||||
"source_file": "lessons/6-Other/22-DeepRL/lab/README.md",
|
||||
"language_code": "zh-TW"
|
||||
},
|
||||
"lessons/6-Other/23-MultiagentSystems/README.md": {
|
||||
"original_hash": "38a1185ae3d54b180378bbd71ae3ef16",
|
||||
"translation_date": "2025-09-23T12:55:20+00:00",
|
||||
"source_file": "lessons/6-Other/23-MultiagentSystems/README.md",
|
||||
"language_code": "zh-TW"
|
||||
},
|
||||
"lessons/6-Other/23-MultiagentSystems/assignment.md": {
|
||||
"original_hash": "cf654ca60c7f86c8dad28596fb42994b",
|
||||
"translation_date": "2025-08-24T22:05:25+00:00",
|
||||
"source_file": "lessons/6-Other/23-MultiagentSystems/assignment.md",
|
||||
"language_code": "zh-TW"
|
||||
},
|
||||
"lessons/7-Ethics/README.md": {
|
||||
"original_hash": "437c988596e751072e41a5aad3fcc5d9",
|
||||
"translation_date": "2025-08-24T21:42:37+00:00",
|
||||
"source_file": "lessons/7-Ethics/README.md",
|
||||
"language_code": "zh-TW"
|
||||
},
|
||||
"lessons/README.md": {
|
||||
"original_hash": "5fef1a0b22498d7188959e2a2cb08af7",
|
||||
"translation_date": "2025-08-24T21:42:27+00:00",
|
||||
"source_file": "lessons/README.md",
|
||||
"language_code": "zh-TW"
|
||||
},
|
||||
"lessons/X-Extras/X1-MultiModal/README.md": {
|
||||
"original_hash": "9c592c26aca16ca085d268c732284187",
|
||||
"translation_date": "2025-08-24T22:06:50+00:00",
|
||||
"source_file": "lessons/X-Extras/X1-MultiModal/README.md",
|
||||
"language_code": "zh-TW"
|
||||
},
|
||||
"lessons/sketchnotes/LICENSE.md": {
|
||||
"original_hash": "45ab63a2cd8f5faef6c9b150618837a4",
|
||||
"translation_date": "2025-08-24T22:12:05+00:00",
|
||||
"source_file": "lessons/sketchnotes/LICENSE.md",
|
||||
"language_code": "zh-TW"
|
||||
},
|
||||
"lessons/sketchnotes/README.md": {
|
||||
"original_hash": "050b8bddebafba55b129414e6ab096ab",
|
||||
"translation_date": "2025-08-24T22:11:06+00:00",
|
||||
"source_file": "lessons/sketchnotes/README.md",
|
||||
"language_code": "zh-TW"
|
||||
},
|
||||
"troubleshoot.md": {
|
||||
"original_hash": "8d9c5a4a7c7798d699672a22cb7fea86",
|
||||
"translation_date": "2025-10-03T09:37:56+00:00",
|
||||
"source_file": "troubleshoot.md",
|
||||
"language_code": "zh-TW"
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,317 @@
|
|||
# AGENTS.md
|
||||
|
||||
## 專案概述
|
||||
|
||||
AI for Beginners 是一個為期 12 週、共 24 節課的全面課程,涵蓋人工智慧的基本概念。本教育資源庫包含使用 Jupyter Notebooks 的實作課程、測驗以及動手實驗。課程內容包括:
|
||||
|
||||
- 符號式 AI:知識表示與專家系統
|
||||
- 使用 TensorFlow 和 PyTorch 的神經網路與深度學習
|
||||
- 電腦視覺技術與架構
|
||||
- 自然語言處理 (NLP):包括 transformers 和 BERT
|
||||
- 專門主題:基因演算法、強化學習、多代理系統
|
||||
- AI 倫理與負責任 AI 原則
|
||||
|
||||
**主要技術:** Python 3、Jupyter Notebooks、TensorFlow、PyTorch、Keras、OpenCV、Vue.js(用於測驗應用程式)
|
||||
|
||||
**架構:** 教育內容資源庫,按主題區域組織 Jupyter Notebooks,並輔以基於 Vue.js 的測驗應用程式及廣泛的多語言支援。
|
||||
|
||||
## 設置指令
|
||||
|
||||
### 主要開發環境 (Python/Jupyter)
|
||||
|
||||
課程設計以 Python 和 Jupyter Notebooks 為基礎。推薦使用 miniconda:
|
||||
|
||||
```bash
|
||||
# Clone the repository
|
||||
git clone https://github.com/microsoft/ai-for-beginners
|
||||
cd ai-for-beginners
|
||||
|
||||
# Create and activate conda environment
|
||||
conda env create --name ai4beg --file environment.yml
|
||||
conda activate ai4beg
|
||||
|
||||
# Start Jupyter Notebook
|
||||
jupyter notebook
|
||||
# OR
|
||||
jupyter lab
|
||||
```
|
||||
|
||||
### 替代方案:使用 devcontainer
|
||||
|
||||
```bash
|
||||
# Open in VS Code and select "Reopen in Container" when prompted
|
||||
# The devcontainer will automatically set up the environment
|
||||
```
|
||||
|
||||
### 測驗應用程式設置
|
||||
|
||||
測驗應用程式是一個獨立的 Vue.js 應用程式,位於 `etc/quiz-app/`:
|
||||
|
||||
```bash
|
||||
cd etc/quiz-app
|
||||
npm install
|
||||
npm run serve # Development server
|
||||
npm run build # Production build
|
||||
npm run lint # Lint and fix files
|
||||
```
|
||||
|
||||
## 開發工作流程
|
||||
|
||||
### 使用 Jupyter Notebooks
|
||||
|
||||
1. **本地開發:**
|
||||
- 啟動 conda 環境:`conda activate ai4beg`
|
||||
- 啟動 Jupyter:`jupyter notebook` 或 `jupyter lab`
|
||||
- 瀏覽至課程資料夾並打開 `.ipynb` 文件
|
||||
- 互動式執行單元以跟隨課程
|
||||
|
||||
2. **使用 VS Code 的 Python 擴展:**
|
||||
- 在 VS Code 中打開資源庫
|
||||
- 安裝 Python 擴展
|
||||
- VS Code 會自動檢測並使用 conda 環境
|
||||
- 直接在 VS Code 中打開 `.ipynb` 文件
|
||||
|
||||
3. **雲端開發:**
|
||||
- **GitHub Codespaces:** 點擊 "Code" → "Codespaces" → "Create codespace on main"
|
||||
- **Binder:** 使用 README 中的 Binder 徽章在瀏覽器中啟動
|
||||
- 注意:Binder 資源有限且有部分網路訪問限制
|
||||
|
||||
### 高級課程的 GPU 支援
|
||||
|
||||
後期課程顯著受益於 GPU 加速:
|
||||
|
||||
- **Azure Data Science VM:** 使用具備 GPU 支援的 NC 系列虛擬機
|
||||
- **Azure Machine Learning:** 使用具 GPU 計算的 notebook 功能
|
||||
- **Google Colab:** 單獨上傳 notebook(提供免費 GPU 支援)
|
||||
|
||||
### 測驗應用程式開發
|
||||
|
||||
```bash
|
||||
cd etc/quiz-app
|
||||
npm run serve # Hot-reload development server at http://localhost:8080
|
||||
```
|
||||
|
||||
## 測試指導
|
||||
|
||||
這是一個以學習內容為重點的教育資源庫,而非傳統的軟體測試。沒有傳統的測試套件。
|
||||
|
||||
### 驗證方法:
|
||||
|
||||
1. **Jupyter Notebooks:** 依序執行單元以驗證程式碼範例是否正常運行
|
||||
2. **測驗應用程式測試:** 通過開發伺服器進行手動測試
|
||||
3. **翻譯驗證:** 檢查 `translations/` 資料夾中的翻譯內容
|
||||
4. **測驗應用程式語法檢查:** 在 `etc/quiz-app/` 中執行 `npm run lint`
|
||||
|
||||
### 執行程式碼範例:
|
||||
|
||||
```bash
|
||||
# Activate environment first
|
||||
conda activate ai4beg
|
||||
|
||||
# Run Python scripts directly
|
||||
python lessons/4-ComputerVision/07-ConvNets/pytorchcv.py
|
||||
|
||||
# Or execute notebooks
|
||||
jupyter notebook lessons/3-NeuralNetworks/03-Perceptron/Perceptron.ipynb
|
||||
```
|
||||
|
||||
## 程式碼風格
|
||||
|
||||
### Python 程式碼風格
|
||||
|
||||
- 遵循標準 Python 規範,適用於教育性程式碼
|
||||
- 清晰、易讀的程式碼,優先考慮學習而非最佳化
|
||||
- 註解解釋關鍵概念
|
||||
- 適合 Jupyter Notebook:單元應盡可能自成一體
|
||||
- 對課程內容無嚴格語法檢查要求
|
||||
|
||||
### JavaScript/Vue.js (測驗應用程式)
|
||||
|
||||
- 在 `etc/quiz-app/package.json` 中配置 ESLint
|
||||
- 執行 `npm run lint` 以檢查並自動修復問題
|
||||
- 遵循 Vue 2.x 規範
|
||||
- 基於元件的架構
|
||||
|
||||
### 文件組織
|
||||
|
||||
```
|
||||
lessons/
|
||||
├── 0-course-setup/ # Setup instructions
|
||||
├── 1-Intro/ # Introduction to AI
|
||||
├── 2-Symbolic/ # Symbolic AI
|
||||
├── 3-NeuralNetworks/ # Neural Networks basics
|
||||
├── 4-ComputerVision/ # Computer Vision
|
||||
├── 5-NLP/ # Natural Language Processing
|
||||
├── 6-Other/ # Other AI techniques
|
||||
├── 7-Ethics/ # AI Ethics
|
||||
└── X-Extras/ # Additional content
|
||||
|
||||
etc/
|
||||
├── quiz-app/ # Vue.js quiz application
|
||||
└── quiz-src/ # Quiz source files
|
||||
|
||||
translations/ # Multi-language translations
|
||||
```
|
||||
|
||||
## 建置與部署
|
||||
|
||||
### Jupyter 內容
|
||||
|
||||
不需要建置過程 - Jupyter Notebooks 可直接執行。
|
||||
|
||||
### 測驗應用程式
|
||||
|
||||
```bash
|
||||
cd etc/quiz-app
|
||||
|
||||
# Development
|
||||
npm run serve
|
||||
|
||||
# Production build
|
||||
npm run build # Outputs to etc/quiz-app/dist/
|
||||
|
||||
# Deploy to Azure Static Web Apps
|
||||
# Azure automatically creates GitHub Actions workflow
|
||||
# See etc/quiz-app/README.md for detailed deployment instructions
|
||||
```
|
||||
|
||||
### 文件網站
|
||||
|
||||
資源庫使用 Docsify 作為文件系統:
|
||||
- `index.html` 作為入口點
|
||||
- 不需要建置 - 直接通過 GitHub Pages 提供服務
|
||||
- 訪問地址:https://microsoft.github.io/AI-For-Beginners/
|
||||
|
||||
## 貢獻指南
|
||||
|
||||
### 拉取請求流程
|
||||
|
||||
1. **標題格式:** 清晰、描述性標題,說明更改內容
|
||||
2. **CLA 要求:** 必須簽署 Microsoft CLA(自動檢查)
|
||||
3. **內容指南:**
|
||||
- 保持教育重點和適合初學者的方式
|
||||
- 測試所有 notebook 中的程式碼範例
|
||||
- 確保 notebook 從頭到尾正常運行
|
||||
- 如果修改英文內容,請更新翻譯
|
||||
4. **測驗應用程式更改:** 在提交前執行 `npm run lint`
|
||||
|
||||
### 翻譯貢獻
|
||||
|
||||
- 翻譯通過 GitHub Actions 使用 co-op-translator 自動完成
|
||||
- 手動翻譯存放於 `translations/<language-code>/`
|
||||
- 測驗翻譯存放於 `etc/quiz-app/src/assets/translations/`
|
||||
- 支援語言:40+ 種語言(完整列表請參閱 README)
|
||||
|
||||
### 活躍貢獻領域
|
||||
|
||||
請參閱 `etc/CONTRIBUTING.md` 了解當前需求:
|
||||
- 深度強化學習部分
|
||||
- 物件檢測改進
|
||||
- 命名實體識別範例
|
||||
- 自定義嵌入訓練範例
|
||||
|
||||
## 環境配置
|
||||
|
||||
### 必需的依賴項
|
||||
|
||||
```bash
|
||||
# Core Python packages (from requirements.txt)
|
||||
tensorflow==2.17.0
|
||||
torch (via conda)
|
||||
torchvision (via conda)
|
||||
keras==3.5.0
|
||||
opencv (via conda)
|
||||
scikit-learn
|
||||
numpy==1.26
|
||||
pandas==2.2.2
|
||||
matplotlib==3.9
|
||||
jupyter
|
||||
```
|
||||
|
||||
### 環境變數
|
||||
|
||||
基本使用不需要特殊環境變數。
|
||||
|
||||
對於 Azure 部署(測驗應用程式):
|
||||
- `AZURE_STATIC_WEB_APPS_API_TOKEN`(由 Azure 自動設置)
|
||||
|
||||
## 除錯與故障排除
|
||||
|
||||
### 常見問題
|
||||
|
||||
**問題:** Conda 環境創建失敗
|
||||
- **解決方案:** 首先更新 conda:`conda update conda -y`
|
||||
- 確保磁碟空間充足(建議 50GB)
|
||||
|
||||
**問題:** 找不到 Jupyter 核心
|
||||
- **解決方案:**
|
||||
```bash
|
||||
conda activate ai4beg
|
||||
python -m ipykernel install --user --name ai4beg
|
||||
```
|
||||
|
||||
**問題:** Notebook 中未檢測到 GPU
|
||||
- **解決方案:**
|
||||
- 驗證 CUDA 安裝:`nvidia-smi`
|
||||
- 檢查 PyTorch GPU:`python -c "import torch; print(torch.cuda.is_available())"`
|
||||
- 檢查 TensorFlow GPU:`python -c "import tensorflow as tf; print(tf.config.list_physical_devices('GPU'))"`
|
||||
|
||||
**問題:** 測驗應用程式無法啟動
|
||||
- **解決方案:**
|
||||
```bash
|
||||
cd etc/quiz-app
|
||||
rm -rf node_modules package-lock.json
|
||||
npm install
|
||||
npm run serve
|
||||
```
|
||||
|
||||
**問題:** Binder 超時或阻止下載
|
||||
- **解決方案:** 使用 GitHub Codespaces 或本地設置以獲得更好的資源訪問
|
||||
|
||||
### 記憶體問題
|
||||
|
||||
某些課程需要大量 RAM(建議 8GB+):
|
||||
- 對於資源密集型課程,使用雲端虛擬機
|
||||
- 執行模型訓練時關閉其他應用程式
|
||||
- 如果記憶體不足,減少 notebook 中的批次大小
|
||||
|
||||
## 附加說明
|
||||
|
||||
### 對課程講師的建議
|
||||
|
||||
- 請參閱 `lessons/0-course-setup/for-teachers.md` 了解教學指導
|
||||
- 課程是自成一體的,可以按順序教授或單獨選擇
|
||||
- 預估時間:12 週,每週 2 節課
|
||||
|
||||
### 雲端資源
|
||||
|
||||
- **Azure for Students:** 學生可獲得免費額度
|
||||
- **Microsoft Learn:** 課程中鏈接的補充學習路徑
|
||||
- **Binder:** 免費但資源有限且有部分網路限制
|
||||
|
||||
### 程式碼執行選項
|
||||
|
||||
1. **本地(推薦):** 完全控制,最佳效能,支援 GPU
|
||||
2. **GitHub Codespaces:** 基於雲端的 VS Code,適合快速訪問
|
||||
3. **Binder:** 基於瀏覽器的 Jupyter,免費但有限
|
||||
4. **Azure ML Notebooks:** 企業選項,支援 GPU
|
||||
5. **Google Colab:** 單獨上傳 notebook,提供免費 GPU 層
|
||||
|
||||
### 使用 Notebook
|
||||
|
||||
- Notebook 設計為逐單元執行以便學習
|
||||
- 許多 notebook 在首次運行時下載數據集(可能需要一些時間)
|
||||
- 某些模型需要 GPU 才能在合理時間內完成訓練
|
||||
- 儘可能使用預訓練模型以減少計算需求
|
||||
|
||||
### 效能考量
|
||||
|
||||
- 後期電腦視覺課程(CNNs、GANs)受益於 GPU
|
||||
- NLP transformer 課程可能需要大量 RAM
|
||||
- 從零開始訓練具有教育意義但耗時
|
||||
- 遷移學習範例可減少訓練時間
|
||||
|
||||
---
|
||||
|
||||
**免責聲明**:
|
||||
本文件使用 AI 翻譯服務 [Co-op Translator](https://github.com/Azure/co-op-translator) 進行翻譯。儘管我們努力確保翻譯的準確性,但請注意,自動翻譯可能包含錯誤或不準確之處。原始文件的母語版本應被視為權威來源。對於關鍵資訊,建議使用專業人工翻譯。我們對因使用此翻譯而引起的任何誤解或錯誤解釋不承擔責任。
|
||||
|
|
@ -0,0 +1,225 @@
|
|||
[](https://github.com/microsoft/AI-For-Beginners/blob/main/LICENSE)
|
||||
[](https://GitHub.com/microsoft/AI-For-Beginners/graphs/contributors/)
|
||||
[](https://GitHub.com/microsoft/AI-For-Beginners/issues/)
|
||||
[](https://GitHub.com/microsoft/AI-For-Beginners/pulls/)
|
||||
[](http://makeapullrequest.com)
|
||||
|
||||
[](https://GitHub.com/microsoft/AI-For-Beginners/watchers/)
|
||||
[](https://GitHub.com/microsoft/AI-For-Beginners/network/)
|
||||
[](https://GitHub.com/microsoft/AI-For-Beginners/stargazers/)
|
||||
[](https://mybinder.org/v2/gh/microsoft/ai-for-beginners/HEAD)
|
||||
[](https://gitter.im/Microsoft/ai-for-beginners?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge)
|
||||
|
||||
[](https://discord.gg/nTYy5BXMWG)
|
||||
|
||||
# 初學者的人工智慧課程
|
||||
|
||||
||
|
||||
|:---:|
|
||||
| AI For Beginners - _手繪筆記由 [@girlie_mac](https://twitter.com/girlie_mac) 製作_ |
|
||||
|
||||
透過我們為期12週、共24課的課程探索**人工智慧**(AI)的世界!課程包含實作教學、測驗及實驗室操作。該課程對初學者友好,涵蓋了 TensorFlow 和 PyTorch 等工具,以及 AI 倫理相關內容
|
||||
|
||||
|
||||
### 🌐 多語言支援
|
||||
|
||||
#### 透過 GitHub Action 支援(自動且持續更新)
|
||||
|
||||
<!-- CO-OP TRANSLATOR LANGUAGES TABLE START -->
|
||||
[阿拉伯文](../ar/README.md) | [孟加拉文](../bn/README.md) | [保加利亞文](../bg/README.md) | [緬甸文 (Myanmar)](../my/README.md) | [中文 (簡體)](../zh-CN/README.md) | [中文 (繁體, 香港)](../zh-HK/README.md) | [中文 (繁體, 澳門)](../zh-MO/README.md) | [中文 (繁體, 台灣)](./README.md) | [克羅埃西亞文](../hr/README.md) | [捷克文](../cs/README.md) | [丹麥文](../da/README.md) | [荷蘭文](../nl/README.md) | [愛沙尼亞文](../et/README.md) | [芬蘭文](../fi/README.md) | [法文](../fr/README.md) | [德文](../de/README.md) | [希臘文](../el/README.md) | [希伯來文](../he/README.md) | [印地文](../hi/README.md) | [匈牙利文](../hu/README.md) | [印尼文](../id/README.md) | [義大利文](../it/README.md) | [日文](../ja/README.md) | [坎納達文](../kn/README.md) | [韓文](../ko/README.md) | [立陶宛文](../lt/README.md) | [馬來文](../ms/README.md) | [馬拉雅拉姆文](../ml/README.md) | [馬拉地文](../mr/README.md) | [尼泊爾文](../ne/README.md) | [奈及利亞皮欽語](../pcm/README.md) | [挪威文](../no/README.md) | [波斯文 (法爾西語)](../fa/README.md) | [波蘭文](../pl/README.md) | [葡萄牙文 (巴西)](../pt-BR/README.md) | [葡萄牙文 (葡萄牙)](../pt-PT/README.md) | [旁遮普文 (Gurmukhi)](../pa/README.md) | [羅馬尼亞文](../ro/README.md) | [俄文](../ru/README.md) | [塞爾維亞文 (西里爾字母)](../sr/README.md) | [斯洛伐克文](../sk/README.md) | [斯洛維尼亞文](../sl/README.md) | [西班牙文](../es/README.md) | [斯瓦希里文](../sw/README.md) | [瑞典文](../sv/README.md) | [他加祿文 (菲律賓)](../tl/README.md) | [泰米爾文](../ta/README.md) | [泰盧固文](../te/README.md) | [泰文](../th/README.md) | [土耳其文](../tr/README.md) | [烏克蘭文](../uk/README.md) | [烏爾都文](../ur/README.md) | [越南文](../vi/README.md)
|
||||
|
||||
> **偏好本機複製?**
|
||||
|
||||
> 本儲存庫包含 50 多種語言的翻譯,會大幅增加下載大小。若想不帶翻譯內容複製,請使用稀疏檢出:
|
||||
> ```bash
|
||||
> git clone --filter=blob:none --sparse https://github.com/microsoft/AI-For-Beginners.git
|
||||
> cd AI-For-Beginners
|
||||
> git sparse-checkout set --no-cone '/*' '!translations' '!translated_images'
|
||||
> ```
|
||||
> 此方式可讓您更快下載完課程所需所有內容。
|
||||
<!-- CO-OP TRANSLATOR LANGUAGES TABLE END -->
|
||||
|
||||
**若您希望支持更多翻譯語言,請參閱 [此處](https://github.com/Azure/co-op-translator/blob/main/getting_started/supported-languages.md)**
|
||||
|
||||
## 加入社群
|
||||
[](https://discord.gg/nTYy5BXMWG)
|
||||
|
||||
## 您將學到什麼
|
||||
|
||||
**[課程心智圖](http://soshnikov.com/courses/ai-for-beginners/mindmap.html)**
|
||||
|
||||
在本課程中,您將學習:
|
||||
|
||||
* 各種人工智慧方法,包括「老派」的符號方法與**知識表示**及推理([GOFAI](https://en.wikipedia.org/wiki/Symbolic_artificial_intelligence))。
|
||||
* **神經網路**與**深度學習**,這是現代 AI 的核心。我們將透過兩個最受歡迎的框架之一用程式碼說明這些重要主題的概念 —— [TensorFlow](http://Tensorflow.org) 與 [PyTorch](http://pytorch.org)。
|
||||
* 用於處理影像與文字的**神經架構**。我們將涵蓋近期模型,但可能不太包括最新尖端技術。
|
||||
* 較不普及的 AI 方法,如**基因演算法**與**多代理系統**。
|
||||
|
||||
本課程不涵蓋內容:
|
||||
|
||||
> [請在 Microsoft Learn 精選中尋找本課程所有額外資源](https://learn.microsoft.com/en-us/collections/7w28iy2xrqzdj0?WT.mc_id=academic-77998-bethanycheum)
|
||||
|
||||
* 關於**AI 在商業上的應用**案例。建議您參加 Microsoft Learn 上的 [商業用戶 AI 入門](https://docs.microsoft.com/learn/paths/introduction-ai-for-business-users/?WT.mc_id=academic-77998-bethanycheum) 學習路徑,或是與 [INSEAD](https://www.insead.edu/) 合作開發的 [AI 商業學院](https://www.microsoft.com/ai/ai-business-school/?WT.mc_id=academic-77998-bethanycheum)。
|
||||
* **經典機器學習**,我們的 [初學者機器學習課程](http://github.com/Microsoft/ML-for-Beginners) 有詳細介紹。
|
||||
* 使用 **[認知服務](https://azure.microsoft.com/services/cognitive-services/?WT.mc_id=academic-77998-bethanycheum)** 的實作 AI 應用。建議從 Microsoft Learn 的 [視覺](https://docs.microsoft.com/learn/paths/create-computer-vision-solutions-azure-cognitive-services/?WT.mc_id=academic-77998-bethanycheum)、[自然語言處理](https://docs.microsoft.com/learn/paths/explore-natural-language-processing/?WT.mc_id=academic-77998-bethanycheum)、**[Azure OpenAI 服務生成式 AI](https://learn.microsoft.com/en-us/training/paths/develop-ai-solutions-azure-openai/?WT.mc_id=academic-77998-bethanycheum)** 模組開始。
|
||||
* 具體的 ML **雲端框架**,如 [Azure Machine Learning](https://azure.microsoft.com/services/machine-learning/?WT.mc_id=academic-77998-bethanycheum)、[Microsoft Fabric](https://learn.microsoft.com/en-us/training/paths/get-started-fabric/?WT.mc_id=academic-77998-bethanycheum)、或 [Azure Databricks](https://docs.microsoft.com/learn/paths/data-engineer-azure-databricks?WT.mc_id=academic-77998-bethanycheum)。您可考慮使用 [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) 與 [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) 學習路徑。
|
||||
* **對話式 AI** 與 **聊天機器人**。另有專門的 [建立對話式 AI 解決方案](https://docs.microsoft.com/learn/paths/create-conversational-ai-solutions/?WT.mc_id=academic-77998-bethanycheum) 學習路徑,也可參考 [此篇部落格文章](https://soshnikov.com/azure/hello-bot-conversational-ai-on-microsoft-platform/) 獲得更多詳情。
|
||||
* 深度學習背後的**深度數學**。推薦書籍為 Ian Goodfellow、Yoshua Bengio 及 Aaron Courville 合著的 [Deep Learning](https://www.amazon.com/Deep-Learning-Adaptive-Computation-Machine/dp/0262035618),並可於線上閱覽 [https://www.deeplearningbook.org/](https://www.deeplearningbook.org/)。
|
||||
|
||||
若想要逐步了解_雲端 AI_,可參考 [Azure 人工智慧快速入門](https://docs.microsoft.com/learn/paths/get-started-with-artificial-intelligence-on-azure/?WT.mc_id=academic-77998-bethanycheum) 學習路徑。
|
||||
|
||||
# 內容
|
||||
|
||||
| | 課程連結 | PyTorch/Keras/TensorFlow | 實驗室 |
|
||||
| :-: | :------------------------------------------------------------------------------------------------------------------------------------------: | :---------------------------------------------------------------------------------------------: | ------------------------------------------------------------------------------ |
|
||||
| 0 | [課程設置](./lessons/0-course-setup/setup.md) | [設置您的開發環境](./lessons/0-course-setup/how-to-run.md) | |
|
||||
| I | [**人工智慧入門**](./lessons/1-Intro/README.md) | | |
|
||||
| 01 | [人工智慧介紹與歷史](./lessons/1-Intro/README.md) | - | - |
|
||||
| II | **符號式 AI** |
|
||||
| 02 | [知識表示與專家系統](./lessons/2-Symbolic/README.md) | [專家系統](./lessons/2-Symbolic/Animals.ipynb) / [本體論](./lessons/2-Symbolic/FamilyOntology.ipynb) /[概念圖](./lessons/2-Symbolic/MSConceptGraph.ipynb) | |
|
||||
| III | [**神經網路導論**](./lessons/3-NeuralNetworks/README.md) |||
|
||||
| 03 | [感知器](./lessons/3-NeuralNetworks/03-Perceptron/README.md) | [筆記本](./lessons/3-NeuralNetworks/03-Perceptron/Perceptron.ipynb) | [實驗室](./lessons/3-NeuralNetworks/03-Perceptron/lab/README.md) |
|
||||
| 04 | [多層感知器與自行架構框架](./lessons/3-NeuralNetworks/04-OwnFramework/README.md) | [筆記本](./lessons/3-NeuralNetworks/04-OwnFramework/OwnFramework.ipynb) | [實驗室](./lessons/3-NeuralNetworks/04-OwnFramework/lab/README.md) |
|
||||
| 05 | [框架導論 (PyTorch/TensorFlow) 與過擬合介紹](./lessons/3-NeuralNetworks/05-Frameworks/README.md) | [PyTorch](./lessons/3-NeuralNetworks/05-Frameworks/IntroPyTorch.ipynb) / [Keras](./lessons/3-NeuralNetworks/05-Frameworks/IntroKeras.ipynb) / [TensorFlow](./lessons/3-NeuralNetworks/05-Frameworks/IntroKerasTF.ipynb) | [實驗室](./lessons/3-NeuralNetworks/05-Frameworks/lab/README.md) |
|
||||
| IV | [**電腦視覺**](./lessons/4-ComputerVision/README.md) | [PyTorch](https://docs.microsoft.com/learn/modules/intro-computer-vision-pytorch/?WT.mc_id=academic-77998-cacaste) / [TensorFlow](https://docs.microsoft.com/learn/modules/intro-computer-vision-TensorFlow/?WT.mc_id=academic-77998-cacaste)| [在 Microsoft Azure 探索電腦視覺](https://learn.microsoft.com/en-us/collections/7w28iy2xrqzdj0?WT.mc_id=academic-77998-bethanycheum) |
|
||||
| 06 | [電腦視覺導論。OpenCV](./lessons/4-ComputerVision/06-IntroCV/README.md) | [筆記本](./lessons/4-ComputerVision/06-IntroCV/OpenCV.ipynb) | [實驗室](./lessons/4-ComputerVision/06-IntroCV/lab/README.md) |
|
||||
| 07 | [卷積神經網絡](./lessons/4-ComputerVision/07-ConvNets/README.md) & [CNN 架構](./lessons/4-ComputerVision/07-ConvNets/CNN_Architectures.md) | [PyTorch](./lessons/4-ComputerVision/07-ConvNets/ConvNetsPyTorch.ipynb) /[TensorFlow](./lessons/4-ComputerVision/07-ConvNets/ConvNetsTF.ipynb) | [實驗室](./lessons/4-ComputerVision/07-ConvNets/lab/README.md) |
|
||||
| 08 | [預訓練網絡與遷移學習](./lessons/4-ComputerVision/08-TransferLearning/README.md) 與 [訓練技巧](./lessons/4-ComputerVision/08-TransferLearning/TrainingTricks.md) | [PyTorch](./lessons/4-ComputerVision/08-TransferLearning/TransferLearningPyTorch.ipynb) / [TensorFlow](./lessons/3-NeuralNetworks/05-Frameworks/IntroKerasTF.ipynb) | [實驗室](./lessons/4-ComputerVision/08-TransferLearning/lab/README.md) |
|
||||
| 09 | [自動編碼器與變分自動編碼器](./lessons/4-ComputerVision/09-Autoencoders/README.md) | [PyTorch](./lessons/4-ComputerVision/09-Autoencoders/AutoEncodersPyTorch.ipynb) / [TensorFlow](./lessons/4-ComputerVision/09-Autoencoders/AutoencodersTF.ipynb) | |
|
||||
| 10 | [生成對抗網絡與藝術風格轉換](./lessons/4-ComputerVision/10-GANs/README.md) | [PyTorch](./lessons/4-ComputerVision/10-GANs/GANPyTorch.ipynb) / [TensorFlow](./lessons/4-ComputerVision/10-GANs/GANTF.ipynb) | |
|
||||
| 11 | [物件偵測](./lessons/4-ComputerVision/11-ObjectDetection/README.md) | [TensorFlow](./lessons/4-ComputerVision/11-ObjectDetection/ObjectDetection.ipynb) | [實驗室](./lessons/4-ComputerVision/11-ObjectDetection/lab/README.md) |
|
||||
| 12 | [語意分割。U-Net](./lessons/4-ComputerVision/12-Segmentation/README.md) | [PyTorch](./lessons/4-ComputerVision/12-Segmentation/SemanticSegmentationPytorch.ipynb) / [TensorFlow](./lessons/4-ComputerVision/12-Segmentation/SemanticSegmentationTF.ipynb) | |
|
||||
| V | [**自然語言處理**](./lessons/5-NLP/README.md) | [PyTorch](https://docs.microsoft.com/learn/modules/intro-natural-language-processing-pytorch/?WT.mc_id=academic-77998-cacaste) /[TensorFlow](https://docs.microsoft.com/learn/modules/intro-natural-language-processing-TensorFlow/?WT.mc_id=academic-77998-cacaste) | [在 Microsoft Azure 探索自然語言處理](https://learn.microsoft.com/en-us/collections/7w28iy2xrqzdj0?WT.mc_id=academic-77998-bethanycheum)|
|
||||
| 13 | [文本表示法。詞袋模型/TF-IDF](./lessons/5-NLP/13-TextRep/README.md) | [PyTorch](https://github.com/microsoft/AI-For-Beginners/blob/main/lessons/5-NLP/13-TextRep/TextRepresentationPyTorch.ipynb) / [TensorFlow](https://github.com/microsoft/AI-For-Beginners/blob/main/lessons/5-NLP/13-TextRep/TextRepresentationTF.ipynb) | |
|
||||
| 14 | [語義詞嵌入。Word2Vec 與 GloVe](./lessons/5-NLP/14-Embeddings/README.md) | [PyTorch](https://github.com/microsoft/AI-For-Beginners/blob/main/lessons/5-NLP/14-Embeddings/EmbeddingsPyTorch.ipynb) / [TensorFlow](https://github.com/microsoft/AI-For-Beginners/blob/main/lessons/5-NLP/14-Embeddings/EmbeddingsTF.ipynb) | |
|
||||
| 15 | [語言模型。訓練你自己的詞嵌入](./lessons/5-NLP/15-LanguageModeling/README.md) | [PyTorch](https://github.com/microsoft/AI-For-Beginners/blob/main/lessons/5-NLP/15-LanguageModeling/CBoW-PyTorch.ipynb) / [TensorFlow](https://github.com/microsoft/AI-For-Beginners/blob/main/lessons/5-NLP/15-LanguageModeling/CBoW-TF.ipynb) | [實驗室](./lessons/5-NLP/15-LanguageModeling/lab/README.md) |
|
||||
| 16 | [遞迴神經網絡](./lessons/5-NLP/16-RNN/README.md) | [PyTorch](https://github.com/microsoft/AI-For-Beginners/blob/main/lessons/5-NLP/16-RNN/RNNPyTorch.ipynb) / [TensorFlow](https://github.com/microsoft/AI-For-Beginners/blob/main/lessons/5-NLP/16-RNN/RNNTF.ipynb) | |
|
||||
| 17 | [生成式遞迴網絡](./lessons/5-NLP/17-GenerativeNetworks/README.md) | [PyTorch](https://github.com/microsoft/AI-For-Beginners/blob/main/lessons/5-NLP/17-GenerativeNetworks/GenerativePyTorch.ipynb) / [TensorFlow](https://github.com/microsoft/AI-For-Beginners/blob/main/lessons/5-NLP/17-GenerativeNetworks/GenerativeTF.ipynb) | [實驗室](./lessons/5-NLP/17-GenerativeNetworks/lab/README.md) |
|
||||
| 18 | [變換器。BERT](./lessons/5-NLP/18-Transformers/README.md) | [PyTorch](https://github.com/microsoft/AI-For-Beginners/blob/main/lessons/5-NLP/18-Transformers/TransformersPyTorch.ipynb) /[TensorFlow](https://github.com/microsoft/AI-For-Beginners/blob/main/lessons/5-NLP/18-Transformers/TransformersTF.ipynb) | |
|
||||
| 19 | [命名實體識別](./lessons/5-NLP/19-NER/README.md) | [TensorFlow](https://microsoft.github.io/AI-For-Beginners/lessons/5-NLP/19-NER/NER-TF.ipynb) | [實驗室](./lessons/5-NLP/19-NER/lab/README.md) |
|
||||
| 20 | [大型語言模型、提示編程與少樣本任務](./lessons/5-NLP/20-LangModels/README.md) | [PyTorch](https://microsoft.github.io/AI-For-Beginners/lessons/5-NLP/20-LangModels/GPT-PyTorch.ipynb) | |
|
||||
| VI | **其他 AI 技術** || |
|
||||
| 21 | [基因演算法](./lessons/6-Other/21-GeneticAlgorithms/README.md) | [筆記本](./lessons/6-Other/21-GeneticAlgorithms/Genetic.ipynb) | |
|
||||
| 22 | [深度強化學習](./lessons/6-Other/22-DeepRL/README.md) | [PyTorch](./lessons/6-Other/22-DeepRL/CartPole-RL-PyTorch.ipynb) /[TensorFlow](./lessons/6-Other/22-DeepRL/CartPole-RL-TF.ipynb) | [實驗室](./lessons/6-Other/22-DeepRL/lab/README.md) |
|
||||
| 23 | [多智能體系統](./lessons/6-Other/23-MultiagentSystems/README.md) | | |
|
||||
| VII | **AI 倫理** | | |
|
||||
| 24 | [AI 倫理與負責任的 AI](./lessons/7-Ethics/README.md) | [Microsoft Learn: 負責任 AI 原則](https://docs.microsoft.com/learn/paths/responsible-ai-business-principles/?WT.mc_id=academic-77998-cacaste) | |
|
||||
| IX | **附錄** | | |
|
||||
| 25 | [多模態網絡、CLIP 與 VQGAN](./lessons/X-Extras/X1-MultiModal/README.md) | [筆記本](./lessons/X-Extras/X1-MultiModal/Clip.ipynb) | |
|
||||
|
||||
## 每堂課包含
|
||||
|
||||
* 預讀資料
|
||||
* 可執行的 Jupyter 筆記本,通常依框架區分(**PyTorch** 或 **TensorFlow**)。可執行的筆記本也包含大量理論內容,理解主題需至少通讀其中一種筆記本(PyTorch 或 TensorFlow)。
|
||||
* 部分主題附有 **實驗室**,讓你有機會嘗試將所學應用到具體問題。
|
||||
* 部分章節提供指向相關主題的 [**MS Learn**](https://learn.microsoft.com/en-us/collections/7w28iy2xrqzdj0?WT.mc_id=academic-77998-bethanycheum) 模組連結。
|
||||
|
||||
## 開始上手
|
||||
|
||||
### 🎯 AI 新手?從這裡開始!
|
||||
|
||||
如果你是 AI 完全初學者,想快速獲得動手範例,請參考我們的 [**初學者友好範例**](./examples/README.md)!其中包含:
|
||||
|
||||
- 🌟 **Hello AI World** - 你的第一個 AI 程式(模式識別)
|
||||
- 🧠 **簡易神經網路** - 從零構建神經網路
|
||||
|
||||
- 🖼️ **圖片分類器** - 使用詳細註解分類圖片
|
||||
- 💬 **文字情感分析** - 分析正面/負面文字
|
||||
|
||||
這些範例旨在幫助您在投入完整課程之前了解 AI 概念。
|
||||
|
||||
### 📚 完整課程設置
|
||||
|
||||
- 我們建立了一個 [設置課程](./lessons/0-course-setup/setup.md),幫助您設定開發環境。 - 對教育工作者,我們也準備了一個 [課程設置課程](./lessons/0-course-setup/for-teachers.md)!
|
||||
- 如何在 VSCode 或 Codespace 中[執行程式碼](./lessons/0-course-setup/how-to-run.md)
|
||||
|
||||
請遵循以下步驟:
|
||||
|
||||
複製此儲存庫:點擊此頁面右上角的「Fork」按鈕。
|
||||
|
||||
複製此儲存庫:`git clone https://github.com/microsoft/AI-For-Beginners.git`
|
||||
|
||||
別忘了為此倉庫點星(🌟),以便日後更容易找到。
|
||||
|
||||
## 認識其他學習者
|
||||
|
||||
加入我們的[官方 AI Discord 伺服器](https://aka.ms/genai-discord?WT.mc_id=academic-105485-bethanycheum),與正在學習此課程的其他學員交流並獲得支援。
|
||||
|
||||
如果您在開發過程中有產品回饋或疑問,請造訪我們的[Azure AI Foundry 開發者論壇](https://aka.ms/foundry/forum)
|
||||
|
||||
## 測驗
|
||||
|
||||
> **關於測驗的說明**:所有測驗都位於 etc\quiz-app 目錄下的 Quiz-app 資料夾中,或可[線上進行](https://ff-quizzes.netlify.app/)。測驗會從課程中連結,可在本地運行或部署到 Azure;請參照 `quiz-app` 資料夾內的指示。測驗內容正逐步在進行多語言在地化。
|
||||
|
||||
## 徵求協助
|
||||
|
||||
您是否有建議或發現拼字或程式碼錯誤?歡迎提出問題或建立 pull request。
|
||||
|
||||
## 特別感謝
|
||||
|
||||
* **✍️ 主要作者:** [Dmitry Soshnikov](http://soshnikov.com), 博士
|
||||
* **🔥 編輯:** [Jen Looper](https://twitter.com/jenlooper), 博士
|
||||
* **🎨 速寫插畫師:** [Tomomi Imura](https://twitter.com/girlie_mac)
|
||||
* **✅ 測驗創作者:** [Lateefah Bello](https://github.com/CinnamonXI), [MLSA](https://studentambassadors.microsoft.com/)
|
||||
* **🙏 核心貢獻者:** [Evgenii Pishchik](https://github.com/Pe4enIks)
|
||||
|
||||
## 其他課程
|
||||
|
||||
我們團隊還有其他課程!請查看:
|
||||
|
||||
<!-- CO-OP TRANSLATOR OTHER COURSES START -->
|
||||
### LangChain
|
||||
[](https://aka.ms/langchain4j-for-beginners)
|
||||
[](https://aka.ms/langchainjs-for-beginners?WT.mc_id=m365-94501-dwahlin)
|
||||
|
||||
---
|
||||
|
||||
### Azure / Edge / MCP / Agents
|
||||
[](https://github.com/microsoft/AZD-for-beginners?WT.mc_id=academic-105485-koreyst)
|
||||
[](https://github.com/microsoft/edgeai-for-beginners?WT.mc_id=academic-105485-koreyst)
|
||||
[](https://github.com/microsoft/mcp-for-beginners?WT.mc_id=academic-105485-koreyst)
|
||||
[](https://github.com/microsoft/ai-agents-for-beginners?WT.mc_id=academic-105485-koreyst)
|
||||
|
||||
---
|
||||
|
||||
### 產生式 AI 系列
|
||||
[](https://github.com/microsoft/generative-ai-for-beginners?WT.mc_id=academic-105485-koreyst)
|
||||
[-9333EA?style=for-the-badge&labelColor=E5E7EB&color=9333EA)](https://github.com/microsoft/Generative-AI-for-beginners-dotnet?WT.mc_id=academic-105485-koreyst)
|
||||
[-C084FC?style=for-the-badge&labelColor=E5E7EB&color=C084FC)](https://github.com/microsoft/generative-ai-for-beginners-java?WT.mc_id=academic-105485-koreyst)
|
||||
[-E879F9?style=for-the-badge&labelColor=E5E7EB&color=E879F9)](https://github.com/microsoft/generative-ai-with-javascript?WT.mc_id=academic-105485-koreyst)
|
||||
|
||||
---
|
||||
|
||||
### 核心學習
|
||||
[](https://aka.ms/ml-beginners?WT.mc_id=academic-105485-koreyst)
|
||||
[](https://aka.ms/datascience-beginners?WT.mc_id=academic-105485-koreyst)
|
||||
[](https://aka.ms/ai-beginners?WT.mc_id=academic-105485-koreyst)
|
||||
[](https://github.com/microsoft/Security-101?WT.mc_id=academic-96948-sayoung)
|
||||
[](https://aka.ms/webdev-beginners?WT.mc_id=academic-105485-koreyst)
|
||||
[](https://aka.ms/iot-beginners?WT.mc_id=academic-105485-koreyst)
|
||||
[](https://github.com/microsoft/xr-development-for-beginners?WT.mc_id=academic-105485-koreyst)
|
||||
|
||||
---
|
||||
|
||||
### Copilot 系列
|
||||
[](https://aka.ms/GitHubCopilotAI?WT.mc_id=academic-105485-koreyst)
|
||||
[](https://github.com/microsoft/mastering-github-copilot-for-dotnet-csharp-developers?WT.mc_id=academic-105485-koreyst)
|
||||
[](https://github.com/microsoft/CopilotAdventures?WT.mc_id=academic-105485-koreyst)
|
||||
<!-- CO-OP TRANSLATOR OTHER COURSES END -->
|
||||
|
||||
## 尋求協助
|
||||
|
||||
如果您遇到困難或對建立 AI 應用有任何問題,請加入與您一同學習的同學和有經驗的開發人員討論 MCP。這是一個支持性社群,歡迎提問並自由分享知識。
|
||||
|
||||
[](https://discord.gg/nTYy5BXMWG)
|
||||
|
||||
如果您在開發時有產品回饋或錯誤,請造訪:
|
||||
|
||||
[](https://aka.ms/foundry/forum)
|
||||
|
||||
---
|
||||
|
||||
<!-- CO-OP TRANSLATOR DISCLAIMER START -->
|
||||
**免責聲明**:
|
||||
本文件使用 AI 翻譯服務 [Co-op Translator](https://github.com/Azure/co-op-translator) 進行翻譯。儘管我們力求準確,但請注意自動翻譯可能包含錯誤或不準確之處。原始語言的文件應視為權威來源。對於重要資訊,建議採用專業人工翻譯。我們不對因使用本翻譯而產生的任何誤解或誤譯負責。
|
||||
<!-- CO-OP TRANSLATOR DISCLAIMER END -->
|
||||
|
|
@ -0,0 +1,40 @@
|
|||
## 安全性
|
||||
|
||||
Microsoft 非常重視我們軟體產品和服務的安全性,包括透過我們的 GitHub 組織管理的所有原始碼庫,這些組織包括 [Microsoft](https://github.com/Microsoft)、[Azure](https://github.com/Azure)、[DotNet](https://github.com/dotnet)、[AspNet](https://github.com/aspnet)、[Xamarin](https://github.com/xamarin) 和 [我們的 GitHub 組織](https://opensource.microsoft.com/)。
|
||||
|
||||
如果您認為在任何 Microsoft 擁有的原始碼庫中發現了符合 [Microsoft 對安全性漏洞的定義](https://aka.ms/opensource/security/definition) 的安全性漏洞,請按照以下說明向我們報告。
|
||||
|
||||
## 報告安全性問題
|
||||
|
||||
**請不要透過公開的 GitHub 問題回報安全性漏洞。**
|
||||
|
||||
相反,請透過 Microsoft Security Response Center (MSRC) 報告,網址為 [https://msrc.microsoft.com/create-report](https://aka.ms/opensource/security/create-report)。
|
||||
|
||||
如果您希望在不登入的情況下提交,請發送電子郵件至 [secure@microsoft.com](mailto:secure@microsoft.com)。如果可能,請使用我們的 PGP 金鑰加密您的訊息;您可以從 [Microsoft Security Response Center PGP Key 頁面](https://aka.ms/opensource/security/pgpkey) 下載。
|
||||
|
||||
您應該會在 24 小時內收到回覆。如果因某些原因未收到,請透過電子郵件跟進,以確保我們已收到您的原始訊息。更多資訊請參閱 [microsoft.com/msrc](https://aka.ms/opensource/security/msrc)。
|
||||
|
||||
請盡可能提供以下所需資訊,以幫助我們更好地了解問題的性質和範圍:
|
||||
|
||||
* 問題類型(例如,緩衝區溢位、SQL 注入、跨站腳本攻擊等)
|
||||
* 與問題表現相關的原始檔案的完整路徑
|
||||
* 受影響原始碼的位置(標籤/分支/提交或直接 URL)
|
||||
* 重現問題所需的任何特殊配置
|
||||
* 重現問題的逐步指導
|
||||
* 概念驗證或利用程式碼(如果可能)
|
||||
* 問題的影響,包括攻擊者可能如何利用該問題
|
||||
|
||||
這些資訊將幫助我們更快速地分類您的報告。
|
||||
|
||||
如果您是為了漏洞賞金計畫而報告,提供更完整的報告可能有助於獲得更高的賞金獎勵。請造訪我們的 [Microsoft Bug Bounty Program](https://aka.ms/opensource/security/bounty) 頁面,了解我們的現行計畫的更多詳情。
|
||||
|
||||
## 偏好語言
|
||||
|
||||
我們偏好所有的溝通以英文進行。
|
||||
|
||||
## 政策
|
||||
|
||||
Microsoft 遵循 [協調式漏洞披露](https://aka.ms/opensource/security/cvd) 原則。
|
||||
|
||||
**免責聲明**:
|
||||
本文件使用 AI 翻譯服務 [Co-op Translator](https://github.com/Azure/co-op-translator) 進行翻譯。我們致力於提供準確的翻譯,但請注意,自動翻譯可能包含錯誤或不準確之處。應以原始語言的文件作為權威來源。對於關鍵資訊,建議尋求專業人工翻譯。我們對於因使用此翻譯而引起的任何誤解或誤讀概不負責。
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
# Microsoft 開源行為準則
|
||||
|
||||
此專案已採用 [Microsoft 開源行為準則](https://opensource.microsoft.com/codeofconduct/)。
|
||||
|
||||
資源:
|
||||
|
||||
- [Microsoft 開源行為準則](https://opensource.microsoft.com/codeofconduct/)
|
||||
- [Microsoft 行為準則常見問題](https://opensource.microsoft.com/codeofconduct/faq/)
|
||||
- 如有疑問或擔憂,請聯繫 [opencode@microsoft.com](mailto:opencode@microsoft.com)
|
||||
|
||||
**免責聲明**:
|
||||
本文件使用 AI 翻譯服務 [Co-op Translator](https://github.com/Azure/co-op-translator) 進行翻譯。我們致力於提供準確的翻譯,但請注意,自動翻譯可能包含錯誤或不準確之處。應以原文文件作為權威來源。對於關鍵資訊,建議尋求專業人工翻譯。我們對因使用此翻譯而引起的任何誤解或錯誤解釋概不負責。
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
# 貢獻指南
|
||||
|
||||
此專案歡迎各種貢獻與建議。大多數的貢獻需要您同意一份貢獻者授權協議 (CLA),以聲明您擁有授權權利並實際授予我們使用您貢獻的權利。詳細資訊請參訪 https://cla.microsoft.com。
|
||||
|
||||
當您提交拉取請求 (pull request) 時,CLA 機器人會自動判斷您是否需要提供 CLA,並適當地標記 PR(例如,添加標籤或評論)。只需按照機器人的指示操作即可。您只需在所有使用我們 CLA 的存儲庫中完成一次此操作。
|
||||
|
||||
此專案已採用 [Microsoft 開源行為準則](https://opensource.microsoft.com/codeofconduct/)。如需更多資訊,請參閱 [行為準則常見問題](https://opensource.microsoft.com/codeofconduct/faq/) 或聯絡 [opencode@microsoft.com](mailto:opencode@microsoft.com) 提出其他問題或意見。
|
||||
|
||||
# 尋求貢獻
|
||||
|
||||
我們目前正在積極尋求以下主題的貢獻:
|
||||
|
||||
- [ ] 撰寫深度強化學習的章節
|
||||
- [ ] 改進物件偵測的章節與筆記本
|
||||
- [ ] PyTorch Lightning(針對[此章節](https://github.com/microsoft/AI-For-Beginners/blob/main/3-NeuralNetworks/05-Frameworks/README.md))
|
||||
- [ ] 撰寫命名實體識別的章節與範例
|
||||
- [ ] 為[此章節](https://github.com/microsoft/AI-For-Beginners/tree/main/5-NLP/15-LanguageModeling)建立訓練自定義嵌入的範例
|
||||
|
||||
**免責聲明**:
|
||||
本文件使用 AI 翻譯服務 [Co-op Translator](https://github.com/Azure/co-op-translator) 進行翻譯。儘管我們努力確保翻譯的準確性,但請注意,自動翻譯可能包含錯誤或不準確之處。原始文件的母語版本應被視為權威來源。對於關鍵資訊,建議尋求專業人工翻譯。我們對因使用此翻譯而引起的任何誤解或錯誤解釋不承擔責任。
|
||||
|
|
@ -0,0 +1,78 @@
|
|||
# 人工智慧 (AI)
|
||||
|
||||
## [人工智慧簡介](https://github.com/microsoft/AI-For-Beginners/blob/main/lessons/1-Intro/README.md)
|
||||
- 人工智慧的定義
|
||||
- 人工智慧的歷史
|
||||
- 人工智慧的方法
|
||||
- 自上而下/符號式
|
||||
- 自下而上/神經網路
|
||||
- 演化式
|
||||
- 協同/湧現式人工智慧
|
||||
- [微軟人工智慧商業學院](https://www.microsoft.com/ai/ai-business-school/?WT.mc_id=academic-77998-cacaste)
|
||||
|
||||
## [符號式人工智慧](https://github.com/microsoft/AI-For-Beginners/blob/main/lessons/2-Symbolic/README.md)
|
||||
- 知識表示
|
||||
- [專家系統](https://github.com/microsoft/AI-For-Beginners/blob/main/lessons/2-Symbolic/Animals.ipynb)
|
||||
- [本體論](https://github.com/microsoft/AI-For-Beginners/blob/main/lessons/2-Symbolic/FamilyOntology.ipynb)
|
||||
- 語義網
|
||||
|
||||
## [神經網路](https://github.com/microsoft/AI-For-Beginners/blob/main/lessons/3-NeuralNetworks/README.md)
|
||||
- [感知器](https://github.com/microsoft/AI-For-Beginners/blob/main/lessons/3-NeuralNetworks/03-Perceptron/README.md)
|
||||
- [多層網路](https://github.com/microsoft/AI-For-Beginners/blob/main/lessons/3-NeuralNetworks/04-OwnFramework/README.md)
|
||||
- [框架簡介](https://github.com/microsoft/AI-For-Beginners/blob/main/lessons/3-NeuralNetworks/05-Frameworks/README.md)
|
||||
- [PyTorch](https://github.com/microsoft/AI-For-Beginners/blob/main/lessons/3-NeuralNetworks/05-Frameworks/IntroPyTorch.ipynb)
|
||||
- [TensorFlow](https://github.com/microsoft/AI-For-Beginners/blob/main/lessons/3-NeuralNetworks/05-Frameworks/IntroKerasTF.md)
|
||||
- [過擬合](https://github.com/microsoft/AI-For-Beginners/blob/main/lessons/3-NeuralNetworks/05-Frameworks/Overfitting.md)
|
||||
|
||||
## [電腦視覺](https://github.com/microsoft/AI-For-Beginners/blob/main/lessons/4-ComputerVision/README.md)
|
||||
- 在微軟學習平台
|
||||
- [人工智慧基礎:探索電腦視覺](https://docs.microsoft.com/learn/paths/explore-computer-vision-microsoft-azure/?WT.mc_id=academic-77998-cacaste)
|
||||
- [使用 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)
|
||||
- [電腦視覺簡介:OpenCV](https://github.com/microsoft/AI-For-Beginners/blob/main/lessons/4-ComputerVision/06-IntroCV/README.md)
|
||||
- [卷積網路](https://github.com/microsoft/AI-For-Beginners/blob/main/lessons/4-ComputerVision/07-ConvNets/README.md)
|
||||
- [卷積神經網路架構](https://github.com/microsoft/AI-For-Beginners/blob/main/lessons/4-ComputerVision/07-ConvNets/CNN_Architectures.md)
|
||||
- [遷移學習](https://github.com/microsoft/AI-For-Beginners/blob/main/lessons/4-ComputerVision/08-TransferLearning/README.md)
|
||||
- [訓練技巧](https://github.com/microsoft/AI-For-Beginners/blob/main/lessons/4-ComputerVision/08-TransferLearning/TrainingTricks.md)
|
||||
- [自編碼器與變分自編碼器 (VAEs)](https://github.com/microsoft/AI-For-Beginners/blob/main/lessons/4-ComputerVision/09-Autoencoders/README.md)
|
||||
- [生成對抗網路 (GANs)](https://github.com/microsoft/AI-For-Beginners/blob/main/lessons/4-ComputerVision/10-GANs/README.md)
|
||||
- [風格遷移](https://github.com/microsoft/AI-For-Beginners/blob/main/lessons/4-ComputerVision/10-GANs/StyleTransfer.ipynb)
|
||||
- [物件偵測](https://github.com/microsoft/AI-For-Beginners/blob/main/lessons/4-ComputerVision/11-ObjectDetection/README.md)
|
||||
- [影像分割](https://github.com/microsoft/AI-For-Beginners/blob/main/lessons/4-ComputerVision/12-Segmentation/README.md)
|
||||
|
||||
## [自然語言處理 (NLP)](https://github.com/microsoft/AI-For-Beginners/blob/main/lessons/5-NLP/README.md)
|
||||
- 在微軟學習平台
|
||||
- [人工智慧基礎:探索自然語言處理](https://docs.microsoft.com/learn/paths/explore-natural-language-processing/?WT.mc_id=academic-77998-cacaste)
|
||||
- [使用 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)
|
||||
- [文本表示](https://github.com/microsoft/AI-For-Beginners/blob/main/lessons/5-NLP/13-TextRep/README.md)
|
||||
- 詞袋模型 (Bag of Words)
|
||||
- TF/IDF
|
||||
- [語義嵌入](https://github.com/microsoft/AI-For-Beginners/blob/main/lessons/5-NLP/14-Embeddings/README.md)
|
||||
- Word2Vec
|
||||
- GloVE
|
||||
- [語言建模](https://github.com/microsoft/AI-For-Beginners/blob/main/lessons/5-NLP/15-LanguageModeling)
|
||||
- [循環神經網路 (RNN)](https://github.com/microsoft/AI-For-Beginners/blob/main/lessons/5-NLP/16-RNN/README.md)
|
||||
- 長短期記憶網路 (LSTM)
|
||||
- 閘控循環單元 (GRU)
|
||||
- [生成式循環網路](https://github.com/microsoft/AI-For-Beginners/blob/main/lessons/5-NLP/17-GenerativeNetworks/README.md)
|
||||
- [Transformer 和 BERT](https://github.com/microsoft/AI-For-Beginners/blob/main/lessons/5-NLP/18-Transformers/README.md)
|
||||
- [命名實體識別 (NER)](https://github.com/microsoft/AI-For-Beginners/blob/main/lessons/5-NLP/19-NER/README.md)
|
||||
- [文本生成與 GPT](https://github.com/microsoft/AI-For-Beginners/blob/main/lessons/5-NLP/20-LanguageModels/README.md)
|
||||
|
||||
## 其他技術
|
||||
- [遺傳演算法](https://github.com/microsoft/AI-For-Beginners/blob/main/lessons/6-Other/21-GeneticAlgorithms/README.md)
|
||||
- [深度強化學習](https://github.com/microsoft/AI-For-Beginners/blob/main/lessons/6-Other/22-DeepRL/README.md)
|
||||
- [多代理系統](https://github.com/microsoft/AI-For-Beginners/blob/main/lessons/6-Other/23-MultiagentSystems/README.md)
|
||||
|
||||
## [人工智慧倫理](https://github.com/microsoft/AI-For-Beginners/blob/main/lessons/7-Ethics/README.md)
|
||||
- [微軟學習平台上的負責任人工智慧](https://docs.microsoft.com/learn/paths/responsible-ai-business-principles/?WT.mc_id=academic-77998-cacaste)
|
||||
|
||||
## 附加內容
|
||||
- [多模態網路](https://github.com/microsoft/AI-For-Beginners/blob/main/lessons/X-Extras/X1-MultiModal/README.md)
|
||||
- [CLIP](https://github.com/microsoft/AI-For-Beginners/blob/main/lessons/X-Extras/X1-MultiModal/Clip.ipynb)
|
||||
- DALL-E
|
||||
- VQ-GAN
|
||||
|
||||
**免責聲明**:
|
||||
本文件使用 AI 翻譯服務 [Co-op Translator](https://github.com/Azure/co-op-translator) 進行翻譯。我們致力於提供準確的翻譯,但請注意,自動翻譯可能包含錯誤或不準確之處。應以原始語言的文件作為權威來源。對於關鍵資訊,建議尋求專業人工翻譯。我們對因使用此翻譯而引起的任何誤解或錯誤解釋概不負責。
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
# 支援
|
||||
|
||||
## 如何提交問題和獲取幫助
|
||||
|
||||
此專案使用 GitHub Issues 來追蹤錯誤和功能請求。在提交新問題之前,請先搜尋現有的問題以避免重複。對於新問題,請將您的錯誤或功能請求提交為一個新的 Issue。
|
||||
|
||||
如果需要幫助或對使用此專案有任何疑問,請使用討論區。
|
||||
|
||||
## Microsoft 支援政策
|
||||
|
||||
對於此專案的支援僅限於上述列出的資源。
|
||||
|
||||
**免責聲明**:
|
||||
本文件使用 AI 翻譯服務 [Co-op Translator](https://github.com/Azure/co-op-translator) 進行翻譯。雖然我們致力於提供準確的翻譯,但請注意,自動翻譯可能包含錯誤或不準確之處。原始文件的母語版本應被視為權威來源。對於關鍵資訊,建議使用專業人工翻譯。我們對因使用此翻譯而引起的任何誤解或錯誤解釋不承擔責任。
|
||||
|
|
@ -0,0 +1,36 @@
|
|||
# 透過翻譯課程來貢獻
|
||||
|
||||
我們歡迎您為本課程的課程內容進行翻譯!
|
||||
|
||||
## 指南
|
||||
|
||||
每個課程資料夾和課程介紹資料夾中都有包含翻譯後的 Markdown 文件的資料夾。
|
||||
|
||||
> 注意,請不要翻譯任何程式碼範例文件中的程式碼;唯一需要翻譯的是 README、作業和測驗。謝謝!
|
||||
|
||||
翻譯後的文件應遵循以下命名規範:
|
||||
|
||||
**README._[language]_.md**
|
||||
|
||||
其中 _[language]_ 是遵循 ISO 639-1 標準的兩位字母語言縮寫(例如,西班牙文為 `README.es.md`,荷蘭文為 `README.nl.md`)。
|
||||
|
||||
**assignment._[language]_.md**
|
||||
|
||||
與 README 類似,請同樣翻譯作業文件。
|
||||
|
||||
**測驗**
|
||||
|
||||
1. 將您的翻譯新增到測驗應用程式中,方法是將文件新增到此處:https://github.com/microsoft/AI-For-Beginners/tree/main/etc/quiz-app/src/assets/translations,並遵循正確的命名規範(如 en.json、fr.json)。**請不要翻譯 'true' 或 'false' 這些詞語,謝謝!**
|
||||
|
||||
2. 將您的語言代碼新增到測驗應用程式的 App.vue 文件中的下拉選單中。
|
||||
|
||||
3. 編輯測驗應用程式的 [translations index.js 文件](https://github.com/microsoft/AI-For-Beginners/blob/main/etc/quiz-app/src/assets/translations/index.js),以新增您的語言。
|
||||
|
||||
4. 最後,編輯您翻譯後的 README.md 文件中的所有測驗連結,直接指向您的翻譯測驗,例如:https://red-field-0a6ddfd03.1.azurestaticapps.net/quiz/1 變更為 https://red-field-0a6ddfd03.1.azurestaticapps.net/quiz/1?loc=id
|
||||
|
||||
**感謝您**
|
||||
|
||||
我們由衷感謝您的努力!
|
||||
|
||||
**免責聲明**:
|
||||
本文件使用 AI 翻譯服務 [Co-op Translator](https://github.com/Azure/co-op-translator) 進行翻譯。雖然我們致力於提供準確的翻譯,但請注意,自動翻譯可能包含錯誤或不準確之處。原始文件的母語版本應被視為權威來源。對於關鍵資訊,建議使用專業人工翻譯。我們對因使用此翻譯而引起的任何誤解或錯誤解釋不承擔責任。
|
||||
|
|
@ -0,0 +1,128 @@
|
|||
# 測驗
|
||||
|
||||
這些測驗是 AI 課程(https://aka.ms/ai-beginners)的課前和課後測驗。
|
||||
|
||||
## 新增翻譯的測驗集
|
||||
|
||||
透過在 `assets/translations` 資料夾中建立相應的測驗結構來新增測驗翻譯。原始測驗位於 `assets/translations/en`。測驗依據課程分為多個組別。請確保編號與正確的測驗部分對齊。整個課程共有 40 個測驗,編號從 0 開始。
|
||||
|
||||
編輯翻譯後,請編輯翻譯資料夾中的 `index.js` 檔案,按照 `en` 中的慣例匯入所有檔案。
|
||||
|
||||
接著,編輯 `assets/translations` 中的 `index.js` 檔案,匯入新的翻譯檔案。
|
||||
|
||||
然後,編輯此應用程式中的 `App.vue` 下拉選單,新增您的語言。將本地化的縮寫與語言資料夾名稱匹配。
|
||||
|
||||
最後,編輯翻譯課程中的所有測驗連結(如果存在),以包含此本地化作為查詢參數,例如:`?loc=fr`。
|
||||
|
||||
## 專案設定
|
||||
|
||||
```
|
||||
npm install
|
||||
```
|
||||
|
||||
### 編譯並熱加載以進行開發
|
||||
|
||||
```
|
||||
npm run serve
|
||||
```
|
||||
|
||||
### 編譯並壓縮以進行生產環境
|
||||
|
||||
```
|
||||
npm run build
|
||||
```
|
||||
|
||||
### 檢查並修復檔案
|
||||
|
||||
```
|
||||
npm run lint
|
||||
```
|
||||
|
||||
### 自訂配置
|
||||
|
||||
請參閱 [配置參考](https://cli.vuejs.org/config/)。
|
||||
|
||||
致謝:感謝此測驗應用程式的原始版本:https://github.com/arpan45/simple-quiz-vue
|
||||
|
||||
## 部署到 Azure
|
||||
|
||||
以下是幫助您開始的逐步指南:
|
||||
|
||||
1. Fork GitHub 儲存庫
|
||||
確保您的靜態網站應用程式程式碼位於您的 GitHub 儲存庫中。Fork 此儲存庫。
|
||||
|
||||
2. 建立 Azure 靜態網站應用程式
|
||||
- 建立 [Azure 帳戶](http://azure.microsoft.com)
|
||||
- 前往 [Azure 入口網站](https://portal.azure.com)
|
||||
- 點擊「建立資源」,然後搜尋「靜態網站應用程式」。
|
||||
- 點擊「建立」。
|
||||
|
||||
3. 配置靜態網站應用程式
|
||||
- 基本設定:
|
||||
- 訂閱:選擇您的 Azure 訂閱。
|
||||
- 資源群組:建立新的資源群組或使用現有的資源群組。
|
||||
- 名稱:為您的靜態網站應用程式提供一個名稱。
|
||||
- 區域:選擇最接近您使用者的區域。
|
||||
|
||||
- #### 部署詳細資訊:
|
||||
- 原始碼:選擇「GitHub」。
|
||||
- GitHub 帳戶:授權 Azure 存取您的 GitHub 帳戶。
|
||||
- 組織:選擇您的 GitHub 組織。
|
||||
- 儲存庫:選擇包含靜態網站應用程式的儲存庫。
|
||||
- 分支:選擇您要部署的分支。
|
||||
|
||||
- #### 建置詳細資訊:
|
||||
- 建置預設值:選擇您的應用程式所使用的框架(例如 React、Angular、Vue 等)。
|
||||
- 應用程式位置:指定包含應用程式程式碼的資料夾(例如,如果在根目錄,則為 /)。
|
||||
- API 位置:如果有 API,請指定其位置(可選)。
|
||||
- 輸出位置:指定建置輸出生成的資料夾(例如 build 或 dist)。
|
||||
|
||||
4. 檢查並建立
|
||||
檢查您的設定,然後點擊「建立」。Azure 將設置必要的資源,並在您的儲存庫中建立 GitHub Actions 工作流程。
|
||||
|
||||
5. GitHub Actions 工作流程
|
||||
Azure 會自動在您的儲存庫中建立一個 GitHub Actions 工作流程檔案(.github/workflows/azure-static-web-apps-<name>.yml)。此工作流程將處理建置和部署過程。
|
||||
|
||||
6. 監控部署
|
||||
前往 GitHub 儲存庫中的「Actions」標籤。
|
||||
您應該會看到一個正在運行的工作流程。此工作流程將建置並部署您的靜態網站應用程式到 Azure。
|
||||
一旦工作流程完成,您的應用程式將在提供的 Azure URL 上線。
|
||||
|
||||
### 範例工作流程檔案
|
||||
|
||||
以下是 GitHub Actions 工作流程檔案的範例:
|
||||
name: Azure Static Web Apps CI/CD
|
||||
```
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
pull_request:
|
||||
types: [opened, synchronize, reopened, closed]
|
||||
branches:
|
||||
- main
|
||||
|
||||
jobs:
|
||||
build_and_deploy_job:
|
||||
runs-on: ubuntu-latest
|
||||
name: Build and Deploy Job
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Build And Deploy
|
||||
id: builddeploy
|
||||
uses: Azure/static-web-apps-deploy@v1
|
||||
with:
|
||||
azure_static_web_apps_api_token: ${{ secrets.AZURE_STATIC_WEB_APPS_API_TOKEN }}
|
||||
repo_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
action: "upload"
|
||||
app_location: "etc/quiz-app # App source code path"
|
||||
api_location: ""API source code path optional
|
||||
output_location: "dist" #Built app content directory - optional
|
||||
```
|
||||
|
||||
### 其他資源
|
||||
- [Azure 靜態網站應用程式文件](https://learn.microsoft.com/azure/static-web-apps/getting-started)
|
||||
- [GitHub Actions 文件](https://docs.github.com/actions/use-cases-and-examples/deploying/deploying-to-azure-static-web-app)
|
||||
|
||||
**免責聲明**:
|
||||
本文件使用 AI 翻譯服務 [Co-op Translator](https://github.com/Azure/co-op-translator) 進行翻譯。我們致力於提供準確的翻譯,但請注意,自動翻譯可能包含錯誤或不準確之處。應以原始語言的文件作為權威來源。對於關鍵資訊,建議尋求專業人工翻譯。我們對因使用此翻譯而引起的任何誤解或誤讀概不負責。
|
||||
|
|
@ -0,0 +1,397 @@
|
|||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"# 簡單的圖像分類器\n",
|
||||
"\n",
|
||||
"此筆記本將教您如何使用預訓練的神經網絡進行圖像分類。\n",
|
||||
"\n",
|
||||
"**您將學到:**\n",
|
||||
"- 如何載入並使用預訓練模型\n",
|
||||
"- 圖像預處理\n",
|
||||
"- 對圖像進行預測\n",
|
||||
"- 理解信心分數\n",
|
||||
"\n",
|
||||
"**使用案例:** 識別圖像中的物體(例如「貓」、「狗」、「車」等)\n",
|
||||
"\n",
|
||||
"---\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## 第一步:匯入所需的函式庫\n",
|
||||
"\n",
|
||||
"讓我們匯入所需的工具。不用擔心,如果你現在還不完全理解這些!\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Core libraries\n",
|
||||
"import numpy as np\n",
|
||||
"from PIL import Image\n",
|
||||
"import requests\n",
|
||||
"from io import BytesIO\n",
|
||||
"\n",
|
||||
"# TensorFlow for deep learning\n",
|
||||
"try:\n",
|
||||
" import tensorflow as tf\n",
|
||||
" from tensorflow.keras.applications import MobileNetV2\n",
|
||||
" from tensorflow.keras.applications.mobilenet_v2 import preprocess_input, decode_predictions\n",
|
||||
" print(\"✅ TensorFlow loaded successfully!\")\n",
|
||||
" print(f\" Version: {tf.__version__}\")\n",
|
||||
"except ImportError:\n",
|
||||
" print(\"❌ Please install TensorFlow: pip install tensorflow\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## 步驟 2:載入預訓練模型\n",
|
||||
"\n",
|
||||
"我們將使用 **MobileNetV2**,這是一個已經在數百萬張圖片上訓練過的神經網絡。\n",
|
||||
"\n",
|
||||
"這被稱為 **遷移學習**——使用別人訓練好的模型!\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"print(\"📦 Loading pre-trained MobileNetV2 model...\")\n",
|
||||
"print(\" This may take a minute on first run (downloading weights)...\")\n",
|
||||
"\n",
|
||||
"# Load the model\n",
|
||||
"# include_top=True means we use the classification layer\n",
|
||||
"# weights='imagenet' means it was trained on ImageNet dataset\n",
|
||||
"model = MobileNetV2(weights='imagenet', include_top=True)\n",
|
||||
"\n",
|
||||
"print(\"✅ Model loaded!\")\n",
|
||||
"print(f\" The model can recognize 1000 different object categories\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## 第三步:輔助函數\n",
|
||||
"\n",
|
||||
"讓我們建立一些函數來載入並準備模型所需的圖片。\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"def load_image_from_url(url):\n",
|
||||
" \"\"\"\n",
|
||||
" Load an image from a URL.\n",
|
||||
" \n",
|
||||
" Args:\n",
|
||||
" url: Web address of the image\n",
|
||||
" \n",
|
||||
" Returns:\n",
|
||||
" PIL Image object\n",
|
||||
" \"\"\"\n",
|
||||
" response = requests.get(url)\n",
|
||||
" img = Image.open(BytesIO(response.content))\n",
|
||||
" return img\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def prepare_image(img):\n",
|
||||
" \"\"\"\n",
|
||||
" Prepare an image for the model.\n",
|
||||
" \n",
|
||||
" Steps:\n",
|
||||
" 1. Resize to 224x224 (model's expected size)\n",
|
||||
" 2. Convert to array\n",
|
||||
" 3. Add batch dimension\n",
|
||||
" 4. Preprocess for MobileNetV2\n",
|
||||
" \n",
|
||||
" Args:\n",
|
||||
" img: PIL Image\n",
|
||||
" \n",
|
||||
" Returns:\n",
|
||||
" Preprocessed image array\n",
|
||||
" \"\"\"\n",
|
||||
" # Resize to 224x224 pixels\n",
|
||||
" img = img.resize((224, 224))\n",
|
||||
" \n",
|
||||
" # Convert to numpy array\n",
|
||||
" img_array = np.array(img)\n",
|
||||
" \n",
|
||||
" # Add batch dimension (model expects multiple images)\n",
|
||||
" img_array = np.expand_dims(img_array, axis=0)\n",
|
||||
" \n",
|
||||
" # Preprocess for MobileNetV2\n",
|
||||
" img_array = preprocess_input(img_array)\n",
|
||||
" \n",
|
||||
" return img_array\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def classify_image(img):\n",
|
||||
" \"\"\"\n",
|
||||
" Classify an image and return top predictions.\n",
|
||||
" \n",
|
||||
" Args:\n",
|
||||
" img: PIL Image\n",
|
||||
" \n",
|
||||
" Returns:\n",
|
||||
" List of (class_name, confidence) tuples\n",
|
||||
" \"\"\"\n",
|
||||
" # Prepare the image\n",
|
||||
" img_array = prepare_image(img)\n",
|
||||
" \n",
|
||||
" # Make prediction\n",
|
||||
" predictions = model.predict(img_array, verbose=0)\n",
|
||||
" \n",
|
||||
" # Decode predictions to human-readable labels\n",
|
||||
" # top=5 means we get the top 5 most likely classes\n",
|
||||
" decoded = decode_predictions(predictions, top=5)[0]\n",
|
||||
" \n",
|
||||
" # Convert to simpler format\n",
|
||||
" results = [(label, float(confidence)) for (_, label, confidence) in decoded]\n",
|
||||
" \n",
|
||||
" return results\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"print(\"✅ Helper functions ready!\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## 步驟 4:使用範例圖片進行測試\n",
|
||||
"\n",
|
||||
"讓我們試著分類一些來自網路的圖片吧!\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Sample images to classify\n",
|
||||
"# These are from Unsplash (free stock photos)\n",
|
||||
"test_images = [\n",
|
||||
" {\n",
|
||||
" \"url\": \"https://images.unsplash.com/photo-1514888286974-6c03e2ca1dba?w=400\",\n",
|
||||
" \"description\": \"A cat\"\n",
|
||||
" },\n",
|
||||
" {\n",
|
||||
" \"url\": \"https://images.unsplash.com/photo-1552053831-71594a27632d?w=400\",\n",
|
||||
" \"description\": \"A dog\"\n",
|
||||
" },\n",
|
||||
" {\n",
|
||||
" \"url\": \"https://images.unsplash.com/photo-1511919884226-fd3cad34687c?w=400\",\n",
|
||||
" \"description\": \"A car\"\n",
|
||||
" },\n",
|
||||
"]\n",
|
||||
"\n",
|
||||
"print(f\"🧪 Testing on {len(test_images)} images...\")\n",
|
||||
"print(\"=\" * 70)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"### 將每張圖片分類\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"for i, img_data in enumerate(test_images, 1):\n",
|
||||
" print(f\"\\n📸 Image {i}: {img_data['description']}\")\n",
|
||||
" print(\"-\" * 70)\n",
|
||||
" \n",
|
||||
" try:\n",
|
||||
" # Load image\n",
|
||||
" img = load_image_from_url(img_data['url'])\n",
|
||||
" \n",
|
||||
" # Display image\n",
|
||||
" display(img.resize((200, 200))) # Show smaller version\n",
|
||||
" \n",
|
||||
" # Classify\n",
|
||||
" results = classify_image(img)\n",
|
||||
" \n",
|
||||
" # Show predictions\n",
|
||||
" print(\"\\n🎯 Top 5 Predictions:\")\n",
|
||||
" for rank, (label, confidence) in enumerate(results, 1):\n",
|
||||
" # Create a visual bar\n",
|
||||
" bar_length = int(confidence * 50)\n",
|
||||
" bar = \"█\" * bar_length\n",
|
||||
" \n",
|
||||
" print(f\" {rank}. {label:20s} {confidence*100:5.2f}% {bar}\")\n",
|
||||
" \n",
|
||||
" except Exception as e:\n",
|
||||
" print(f\"❌ Error: {e}\")\n",
|
||||
"\n",
|
||||
"print(\"\\n\" + \"=\" * 70)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## 第五步:嘗試使用自己的圖片!\n",
|
||||
"\n",
|
||||
"將下面的 URL 替換為您想要分類的任何圖片 URL。\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Try your own image!\n",
|
||||
"# Replace this URL with any image URL\n",
|
||||
"custom_image_url = \"https://images.unsplash.com/photo-1472491235688-bdc81a63246e?w=400\" # A flower\n",
|
||||
"\n",
|
||||
"print(\"🖼️ Classifying your custom image...\")\n",
|
||||
"print(\"=\" * 70)\n",
|
||||
"\n",
|
||||
"try:\n",
|
||||
" # Load and show image\n",
|
||||
" img = load_image_from_url(custom_image_url)\n",
|
||||
" display(img.resize((300, 300)))\n",
|
||||
" \n",
|
||||
" # Classify\n",
|
||||
" results = classify_image(img)\n",
|
||||
" \n",
|
||||
" # Show results\n",
|
||||
" print(\"\\n🎯 Top 5 Predictions:\")\n",
|
||||
" print(\"-\" * 70)\n",
|
||||
" for rank, (label, confidence) in enumerate(results, 1):\n",
|
||||
" bar_length = int(confidence * 50)\n",
|
||||
" bar = \"█\" * bar_length\n",
|
||||
" print(f\" {rank}. {label:20s} {confidence*100:5.2f}% {bar}\")\n",
|
||||
" \n",
|
||||
" # Highlight top prediction\n",
|
||||
" top_label, top_confidence = results[0]\n",
|
||||
" print(\"\\n\" + \"=\" * 70)\n",
|
||||
" print(f\"\\n🏆 Best guess: {top_label} ({top_confidence*100:.2f}% confident)\")\n",
|
||||
" \n",
|
||||
"except Exception as e:\n",
|
||||
" print(f\"❌ Error: {e}\")\n",
|
||||
" print(\" Make sure the URL points to a valid image!\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## 💡 剛剛發生了什麼?\n",
|
||||
"\n",
|
||||
"1. **我們載入了一個預訓練模型** - MobileNetV2 已經在數百萬張圖片上進行過訓練 \n",
|
||||
"2. **我們對圖片進行了預處理** - 將圖片調整大小並格式化以適配模型 \n",
|
||||
"3. **模型進行了預測** - 輸出了對 1000 個物件類別的概率 \n",
|
||||
"4. **我們解碼了結果** - 將數字轉換為人類可讀的標籤 \n",
|
||||
"\n",
|
||||
"### 理解信心分數\n",
|
||||
"\n",
|
||||
"- **90-100%**:非常有信心(幾乎肯定正確) \n",
|
||||
"- **70-90%**:有信心(可能正確) \n",
|
||||
"- **50-70%**:有些信心(可能正確) \n",
|
||||
"- **低於 50%**:信心不足(不確定) \n",
|
||||
"\n",
|
||||
"### 為什麼預測可能會出錯?\n",
|
||||
"\n",
|
||||
"- **不尋常的角度或光線** - 模型是在典型照片上訓練的 \n",
|
||||
"- **多個物件** - 模型預期只有一個主要物件 \n",
|
||||
"- **罕見物件** - 模型只認識 1000 個類別 \n",
|
||||
"- **低品質圖片** - 模糊或像素化的圖片更難辨識 \n",
|
||||
"\n",
|
||||
"---\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## 🚀 下一步\n",
|
||||
"\n",
|
||||
"1. **嘗試不同的圖片:**\n",
|
||||
" - 在 [Unsplash](https://unsplash.com) 上尋找圖片\n",
|
||||
" - 右鍵點擊 → 選擇「複製圖片地址」以獲取 URL\n",
|
||||
"\n",
|
||||
"2. **進行實驗:**\n",
|
||||
" - 使用抽象藝術會發生什麼?\n",
|
||||
" - 它能否識別不同角度的物體?\n",
|
||||
" - 它如何處理多個物體?\n",
|
||||
"\n",
|
||||
"3. **深入學習:**\n",
|
||||
" - 探索 [電腦視覺課程](../lessons/4-ComputerVision/README.md)\n",
|
||||
" - 學習如何訓練自己的圖像分類器\n",
|
||||
" - 理解 CNN(卷積神經網絡)的工作原理\n",
|
||||
"\n",
|
||||
"---\n",
|
||||
"\n",
|
||||
"## 🎉 恭喜!\n",
|
||||
"\n",
|
||||
"你剛剛使用最先進的神經網絡構建了一個圖像分類器!\n",
|
||||
"\n",
|
||||
"這種技術同樣應用於:\n",
|
||||
"- Google Photos(整理你的照片)\n",
|
||||
"- 自駕車(識別物體)\n",
|
||||
"- 醫學診斷(分析 X 光片)\n",
|
||||
"- 品質控制(檢測缺陷)\n",
|
||||
"\n",
|
||||
"繼續探索和學習吧!🚀\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"\n---\n\n**免責聲明**: \n本文件已使用 AI 翻譯服務 [Co-op Translator](https://github.com/Azure/co-op-translator) 進行翻譯。儘管我們致力於提供準確的翻譯,請注意自動翻譯可能包含錯誤或不準確之處。原始文件的母語版本應被視為權威來源。對於關鍵資訊,建議使用專業人工翻譯。我們對因使用此翻譯而引起的任何誤解或錯誤解釋不承擔責任。\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"kernelspec": {
|
||||
"display_name": "Python 3",
|
||||
"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.0"
|
||||
},
|
||||
"coopTranslator": {
|
||||
"original_hash": "1d472141d9df46b751542b3c29f88677",
|
||||
"translation_date": "2025-10-03T11:40:39+00:00",
|
||||
"source_file": "examples/03-image-classifier.ipynb",
|
||||
"language_code": "tw"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 4
|
||||
}
|
||||
|
|
@ -0,0 +1,85 @@
|
|||
# 初學者友善的 AI 範例
|
||||
|
||||
歡迎!此目錄包含簡單、獨立的範例,幫助您開始學習 AI 和機器學習。每個範例都設計為適合初學者,並附有詳細的註解和逐步解說。
|
||||
|
||||
## 📚 範例概覽
|
||||
|
||||
| 範例 | 描述 | 難度 | 先決條件 |
|
||||
|------|------|------|----------|
|
||||
| [Hello AI World](../../../examples/01-hello-ai-world.py) | 您的第一個 AI 程式 - 簡單的模式識別 | ⭐ 初學者 | Python 基礎 |
|
||||
| [Simple Neural Network](../../../examples/02-simple-neural-network.py) | 從零開始建立神經網路 | ⭐⭐ 初學者+ | Python、基礎數學 |
|
||||
| [Image Classifier](./03-image-classifier.ipynb) | 使用預訓練模型進行影像分類 | ⭐⭐ 初學者+ | Python、numpy |
|
||||
| [Text Sentiment](../../../examples/04-text-sentiment.py) | 分析文字情感(正面/負面) | ⭐⭐ 初學者+ | Python |
|
||||
|
||||
## 🚀 開始使用
|
||||
|
||||
### 先決條件
|
||||
|
||||
請確保您已安裝 Python(建議使用 3.8 或更高版本)。安裝所需的套件:
|
||||
|
||||
```bash
|
||||
# For Python scripts
|
||||
pip install numpy
|
||||
|
||||
# For Jupyter notebooks (image classifier)
|
||||
pip install jupyter numpy pillow tensorflow
|
||||
```
|
||||
|
||||
或者使用主課程中的 conda 環境:
|
||||
|
||||
```bash
|
||||
conda env create --name ai4beg --file ../environment.yml
|
||||
conda activate ai4beg
|
||||
```
|
||||
|
||||
### 執行範例
|
||||
|
||||
**對於 Python 腳本 (.py 檔案):**
|
||||
```bash
|
||||
python 01-hello-ai-world.py
|
||||
```
|
||||
|
||||
**對於 Jupyter 筆記本 (.ipynb 檔案):**
|
||||
```bash
|
||||
jupyter notebook 03-image-classifier.ipynb
|
||||
```
|
||||
|
||||
## 📖 學習路徑
|
||||
|
||||
我們建議按照以下順序進行學習:
|
||||
|
||||
1. **從 "Hello AI World" 開始** - 學習模式識別的基礎
|
||||
2. **建立簡單的神經網路** - 理解神經網路的運作方式
|
||||
3. **嘗試影像分類器** - 使用真實影像體驗 AI 的應用
|
||||
4. **分析文字情感** - 探索自然語言處理
|
||||
|
||||
## 💡 初學者提示
|
||||
|
||||
- **仔細閱讀程式碼註解** - 它們解釋了每行程式碼的作用
|
||||
- **嘗試實驗!** - 嘗試更改數值並觀察結果
|
||||
- **不用擔心完全理解** - 學習需要時間
|
||||
- **提出問題** - 使用 [討論板](https://github.com/microsoft/AI-For-Beginners/discussions)
|
||||
|
||||
## 🔗 下一步
|
||||
|
||||
完成這些範例後,探索完整課程:
|
||||
- [AI 簡介](../lessons/1-Intro/README.md)
|
||||
- [神經網路](../lessons/3-NeuralNetworks/README.md)
|
||||
- [電腦視覺](../lessons/4-ComputerVision/README.md)
|
||||
- [自然語言處理](../lessons/5-NLP/README.md)
|
||||
|
||||
## 🤝 貢獻
|
||||
|
||||
覺得這些範例有幫助嗎?幫助我們改進:
|
||||
- 回報問題或提出改進建議
|
||||
- 增加更多適合初學者的範例
|
||||
- 改善文件和註解
|
||||
|
||||
---
|
||||
|
||||
*記住:每位專家都曾是初學者。祝學習愉快! 🎓*
|
||||
|
||||
---
|
||||
|
||||
**免責聲明**:
|
||||
本文件已使用 AI 翻譯服務 [Co-op Translator](https://github.com/Azure/co-op-translator) 進行翻譯。雖然我們致力於提供準確的翻譯,但請注意,自動翻譯可能包含錯誤或不準確之處。原始文件的母語版本應被視為權威來源。對於關鍵資訊,建議使用專業人工翻譯。我們對因使用此翻譯而引起的任何誤解或誤釋不承擔責任。
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
# 給教育工作者
|
||||
|
||||
想在您的課堂上使用這份課程嗎?請隨意使用!
|
||||
|
||||
事實上,您可以直接在 GitHub 上使用它,通過 GitHub Classroom 來實現。
|
||||
|
||||
要做到這一點,請先 fork 這個倉庫。您需要為每一課創建一個倉庫,因此需要將每個文件夾提取到一個單獨的倉庫中。這樣,[GitHub Classroom](https://classroom.github.com/classrooms) 就可以分別處理每一課。
|
||||
|
||||
這些[完整的說明](https://github.blog/2020-03-18-set-up-your-digital-classroom-with-github-classroom/)可以幫助您了解如何設置您的課堂。
|
||||
|
||||
## 按原樣使用此倉庫
|
||||
|
||||
如果您希望按目前的形式使用此倉庫,而不使用 GitHub Classroom,也可以這樣做。您需要與您的學生溝通,告訴他們一起學習哪一課。
|
||||
|
||||
在線上課程形式中(如 Zoom、Teams 或其他工具),您可以為測驗設置分組討論室,並指導學生為學習做好準備。然後邀請學生參加測驗,並在特定時間以 "issues" 的形式提交他們的答案。如果您希望學生公開協作完成作業,也可以採用相同的方式。
|
||||
|
||||
如果您更喜歡更私密的形式,可以要求您的學生逐課 fork 這份課程到他們自己的 GitHub 私人倉庫,並授予您訪問權限。然後,他們可以私下完成測驗和作業,並通過您課堂倉庫中的 issues 提交給您。
|
||||
|
||||
在在線課堂形式中,有很多方法可以讓這個課程運作起來。請告訴我們哪種方式最適合您!
|
||||
|
||||
## 請分享您的想法
|
||||
|
||||
我們希望這份課程能夠滿足您和您學生的需求。請在討論區中給我們反饋!
|
||||
|
||||
**免責聲明**:
|
||||
本文件使用 AI 翻譯服務 [Co-op Translator](https://github.com/Azure/co-op-translator) 進行翻譯。雖然我們致力於提供準確的翻譯,但請注意,自動翻譯可能包含錯誤或不準確之處。原始文件的母語版本應被視為權威來源。對於關鍵資訊,建議使用專業人工翻譯。我們對因使用此翻譯而引起的任何誤解或錯誤解釋不承擔責任。
|
||||
|
|
@ -0,0 +1,71 @@
|
|||
# 如何執行程式碼
|
||||
|
||||
本課程包含許多可執行的範例和實驗,你會想要運行這些範例。為此,你需要具備在本課程所提供的 Jupyter 筆記本中執行 Python 程式碼的能力。你有幾種選擇可以執行程式碼:
|
||||
|
||||
## 在你的電腦上本地執行
|
||||
|
||||
若要在你的電腦本地運行程式碼,需要安裝 Python。一個建議是安裝 **[miniconda](https://conda.io/en/latest/miniconda.html)** — 它是一個相當輕量的安裝,支援 `conda` 套件管理工具來管理不同的 Python **虛擬環境**。
|
||||
|
||||
安裝 miniconda 之後,複製本儲存庫並建立一個虛擬環境來使用這門課程:
|
||||
|
||||
```bash
|
||||
git clone http://github.com/microsoft/ai-for-beginners
|
||||
cd ai-for-beginners
|
||||
conda env create --name ai4beg --file .devcontainer/environment.yml
|
||||
conda activate ai4beg
|
||||
```
|
||||
|
||||
### 使用帶有 Python 擴充功能的 Visual Studio Code
|
||||
|
||||
本課程在使用 [Visual Studio Code](http://code.visualstudio.com/?WT.mc_id=academic-77998-cacaste) 並安裝 [Python 擴充功能](https://marketplace.visualstudio.com/items?itemName=ms-python.python&WT.mc_id=academic-77998-cacaste) 時效果最佳。
|
||||
|
||||
> **注意**:一旦你複製儲存庫並在 VS Code 中打開目錄,它會自動建議你安裝 Python 擴充功能。你還需要按上述說明安裝 miniconda。
|
||||
|
||||
> **注意**:如果 VS Code 建議你在容器中重新打開儲存庫,你應該拒絕此建議以使用本地的 Python 安裝。
|
||||
|
||||
### 在瀏覽器中使用 Jupyter
|
||||
|
||||
你也可以在自己的電腦瀏覽器中使用 Jupyter 環境。傳統的 Jupyter 和 JupyterHub 都提供方便的開發環境,具備自動完成、程式碼高亮等功能。
|
||||
|
||||
要在本地啟動 Jupyter,請前往課程目錄,並執行:
|
||||
|
||||
```bash
|
||||
jupyter notebook
|
||||
```
|
||||
或
|
||||
```bash
|
||||
jupyterhub
|
||||
```
|
||||
然後你可以瀏覽任何 `.ipynb` 檔案,打開它們並開始工作。
|
||||
|
||||
### 在容器中執行
|
||||
|
||||
另一個替代 Python 安裝的選項是直接在容器中執行程式碼。由於我們的儲存庫提供了一個特殊的 `.devcontainer` 資料夾,指示如何為本儲存庫建構容器,VS Code 提供重新在容器中打開程式碼的功能。這需要安裝 Docker,流程也會較複雜,因此建議較有經驗的使用者採用此方案。
|
||||
|
||||
## 在雲端運行
|
||||
|
||||
如果你不想在本地安裝 Python,並且有雲端資源可用,另一個好選擇是直接在雲端執行程式碼。有幾種方式可以做到:
|
||||
|
||||
* 使用 **[GitHub Codespaces](https://github.com/features/codespaces)**,這是在 GitHub 上為你建立的虛擬環境,可經由 VS Code 瀏覽器介面存取。如果你有 Codespaces 的權限,只需點擊儲存庫中的 **Code** 按鈕,啟動 codespace,馬上即可開始執行。
|
||||
* 使用 **[Binder](https://mybinder.org/v2/gh/microsoft/ai-for-beginners/HEAD)**。[Binder](https://mybinder.org) 提供免費的雲端運算資源,讓你可以測試 GitHub 上的程式碼。首頁有一個按鈕可直接開啟儲存庫於 Binder — 這會快速引導你到 Binder 網站,自動建構底層容器並啟動 Jupyter 網頁介面。
|
||||
|
||||
> **注意**:為防止濫用,Binder 對部分網路資源進行封鎖,這可能導致部分從公共網路下載模型和/或資料集的程式碼無法正常運作,你可能需找尋替代方案。此外,Binder 提供的運算資源相當基礎,訓練過程會較慢,尤其是後期更複雜的課程。
|
||||
|
||||
## 使用 GPU 雲端運行
|
||||
|
||||
在本課程後期,有些課程會非常受惠於 GPU 支援。例如,模型訓練否則會非常緩慢。以下是一些選項,特別是如果你透過 [Azure for Students](https://azure.microsoft.com/free/students/?WT.mc_id=academic-77998-cacaste) 或機構有雲端存取權:
|
||||
|
||||
* 建立 [資料科學虛擬機](https://docs.microsoft.com/learn/modules/intro-to-azure-data-science-virtual-machine/?WT.mc_id=academic-77998-cacaste) 並透過 Jupyter 連線。你可以直接在機器上複製儲存庫並開始學習。NC 系列虛擬機支援 GPU。
|
||||
|
||||
> **注意**:部分訂閱,包括 Azure for Students,預設不包含 GPU 支援。你可能需要透過技術支援申請額外的 GPU 核心。
|
||||
|
||||
* 建立 [Azure 機器學習工作區](https://azure.microsoft.com/services/machine-learning/?WT.mc_id=academic-77998-cacaste),然後使用其中的 Notebook 功能。[此影片](https://azure-for-academics.github.io/quickstart/azureml-papers/) 示範如何將儲存庫克隆到 Azure ML 筆記本並開始使用。
|
||||
|
||||
你也可以使用 Google Colab,該服務附帶部分免費的 GPU 支援,並上傳 Jupyter 筆記本,一筆一筆地執行。
|
||||
|
||||
---
|
||||
|
||||
<!-- CO-OP TRANSLATOR DISCLAIMER START -->
|
||||
**免責聲明**:
|
||||
本文件係使用 AI 翻譯服務 [Co-op Translator](https://github.com/Azure/co-op-translator) 進行翻譯。雖然我們努力追求準確性,但請注意,自動翻譯可能包含錯誤或不精確之處。原始文件的母語版本應視為權威資料來源。對於重要資訊,建議採用專業人工翻譯。我們對因使用本翻譯所引起的任何誤解或誤譯不承擔任何責任。
|
||||
<!-- CO-OP TRANSLATOR DISCLAIMER END -->
|
||||
|
|
@ -0,0 +1,49 @@
|
|||
# 開始使用這份課程
|
||||
|
||||
## 你是學生嗎?
|
||||
|
||||
可以從以下資源開始:
|
||||
|
||||
* [學生中心頁面](https://docs.microsoft.com/learn/student-hub?WT.mc_id=academic-77998-cacaste) 在這個頁面,你可以找到初學者資源、學生套件,甚至有機會獲得免費認證券。這是一個值得收藏並定期查看的頁面,因為我們至少每月更新內容。
|
||||
* [Microsoft 學生學習大使](https://studentambassadors.microsoft.com?WT.mc_id=academic-77998-cacaste) 加入一個全球性的學生大使社群,這可能是你進入 Microsoft 的途徑。
|
||||
|
||||
**學生們**,有幾種方式可以使用這份課程。首先,你可以直接在 GitHub 上閱讀文本並查看程式碼。如果你想在任何筆記本中執行程式碼 - [閱讀我們的指導](./how-to-run.md),並在[這篇部落格文章](https://soshnikov.com/education/how-to-execute-notebooks-from-github/)中找到更多建議。
|
||||
|
||||
> **Note**: [如何執行這份課程中的程式碼的指導](./how-to-run.md)
|
||||
|
||||
## 自學
|
||||
|
||||
然而,如果你想將這門課程作為自學專案,我們建議你將整個倉庫 fork 到自己的 GitHub 帳戶,並自行或與小組一起完成練習:
|
||||
|
||||
* 從課前測驗開始。
|
||||
* 閱讀課程的介紹文本。
|
||||
* 如果課程有額外的筆記本,請逐一閱讀並執行程式碼。如果同時提供 TensorFlow 和 PyTorch 筆記本,你可以專注於其中一個 - 選擇你喜歡的框架。
|
||||
* 筆記本通常包含一些挑戰,要求你稍微修改程式碼以進行實驗。
|
||||
* 完成課後測驗。
|
||||
* 如果模組附有實驗室,請完成作業。
|
||||
* 造訪 [討論板](https://github.com/microsoft/AI-For-Beginners/discussions) 以「大聲學習」。
|
||||
|
||||
> 為了進一步學習,我們建議你參考這些 [Microsoft Learn](https://docs.microsoft.com/en-us/users/dmitrysoshnikov-9132/collections/31zgizg2p418yo/?WT.mc_id=academic-77998-cacaste) 模組和學習路徑。
|
||||
|
||||
**教師們**,我們提供了一些[建議](./for-teachers.md)來幫助你使用這份課程。
|
||||
|
||||
---
|
||||
|
||||
## 教學法
|
||||
|
||||
在設計這份課程時,我們選擇了兩個教學原則:確保它是**基於專案**的實作,並且包含**頻繁的測驗**。
|
||||
|
||||
透過確保內容與專案相符,學習過程對學生來說更具吸引力,並能增強概念的記憶。此外,課前的低壓測驗能讓學生專注於學習主題,而課後的第二次測驗則能進一步加強記憶。這份課程設計靈活有趣,可以完整學習或部分選用。專案從小型開始,並在 12 週的循環中逐漸變得更複雜。
|
||||
|
||||
> **關於測驗的說明**:所有測驗都包含在[這個應用程式](https://red-field-0a6ddfd03.1.azurestaticapps.net/)中,共有 50 個測驗,每個測驗包含三個問題。測驗在課程中有連結,但測驗應用程式也可以在本地執行;請按照 `etc/quiz-app` 資料夾中的指導進行。
|
||||
|
||||
## 離線存取
|
||||
|
||||
你可以使用 [Docsify](https://docsify.js.org/#/) 離線執行這份文件。Fork 這個倉庫,在本地機器上[安裝 Docsify](https://docsify.js.org/#/quickstart),然後在倉庫的根目錄中輸入 `docsify serve`。網站將在本地端的 3000 埠上提供服務:`localhost:3000`。課程的 PDF 可在[此連結](../../../../../../../../../etc/pdf/readme.pdf)中取得。
|
||||
|
||||
---
|
||||
|
||||
<!-- CO-OP TRANSLATOR DISCLAIMER START -->
|
||||
**免責聲明**:
|
||||
本文件已使用 AI 翻譯服務 [Co-op Translator](https://github.com/Azure/co-op-translator) 進行翻譯。儘管我們致力於提供準確的翻譯,請注意自動翻譯可能包含錯誤或不準確之處。原始文件的母語版本應被視為權威來源。對於關鍵資訊,建議使用專業人工翻譯。我們對於因使用此翻譯而引起的任何誤解或誤釋不承擔責任。
|
||||
<!-- CO-OP TRANSLATOR DISCLAIMER END -->
|
||||
|
|
@ -0,0 +1,161 @@
|
|||
# 人工智慧簡介
|
||||
|
||||

|
||||
|
||||
> 手繪筆記由 [Tomomi Imura](https://twitter.com/girlie_mac) 提供
|
||||
|
||||
## [課前測驗](https://ff-quizzes.netlify.app/en/ai/quiz/1)
|
||||
|
||||
**人工智慧**是一門令人興奮的科學領域,研究如何讓電腦展現出智能行為,例如執行人類擅長的事情。
|
||||
|
||||
最初,電腦由 [Charles Babbage](https://en.wikipedia.org/wiki/Charles_Babbage) 發明,用來根據明確定義的程序(演算法)操作數字。現代電腦雖然比19世紀提出的原型複雜得多,但仍然遵循受控計算的理念。因此,只要我們知道達成目標所需的精確步驟,就可以編程讓電腦完成某些事情。
|
||||
|
||||

|
||||
|
||||
> 照片由 [Vickie Soshnikova](http://twitter.com/vickievalerie) 提供
|
||||
|
||||
> ✅ 從一張照片中判斷一個人的年齡是一項無法明確編程的任務,因為我們不知道自己在腦海中得出數字的具體過程。
|
||||
|
||||
---
|
||||
|
||||
然而,有些任務我們並不明確知道如何解決。比如從一張照片中判斷一個人的年齡。我們能夠做到這件事,是因為我們看過許多不同年齡的人的例子,但我們無法明確解釋自己是如何做到的,也無法編程讓電腦完成這件事。這正是**人工智慧**(簡稱 AI)感興趣的任務類型。
|
||||
|
||||
✅ 想一想有哪些任務可以交給電腦處理,並且能從 AI 中受益。考慮金融、醫療和藝術領域——這些領域今天如何從 AI 中受益?
|
||||
|
||||
## 弱人工智慧 vs. 強人工智慧
|
||||
|
||||
弱人工智慧 | 強人工智慧
|
||||
---------------------------------------|-------------------------------------
|
||||
弱人工智慧指的是設計和訓練用於特定任務或狹窄任務集的 AI 系統。|強人工智慧,或稱人工通用智慧(AGI),指的是具有人類水平智能和理解能力的 AI 系統。
|
||||
這些 AI 系統並不具備一般智能;它們擅長執行預定義的任務,但缺乏真正的理解或意識。|這些 AI 系統能夠執行人類可以完成的任何智力任務,適應不同領域,並擁有某種形式的意識或自我認知。
|
||||
弱人工智慧的例子包括虛擬助理如 Siri 或 Alexa、流媒體服務使用的推薦算法,以及專門設計用於客戶服務的聊天機器人。|實現強人工智慧是 AI 研究的長期目標,需要開發能夠在廣泛任務和情境中進行推理、學習、理解和適應的 AI 系統。
|
||||
弱人工智慧高度專業化,並不具備人類般的認知能力或超出其狹窄領域的一般問題解決能力。|強人工智慧目前仍是一個理論概念,尚無 AI 系統達到這種通用智能的水平。
|
||||
|
||||
更多資訊請參考 **[人工通用智慧](https://en.wikipedia.org/wiki/Artificial_general_intelligence)** (AGI)。
|
||||
|
||||
## 智能的定義與圖靈測試
|
||||
|
||||
在討論**[智能](https://en.wikipedia.org/wiki/Intelligence)**這個術語時,其中一個問題是我們對此並沒有明確的定義。有人可能認為智能與**抽象思維**或**自我意識**相關,但我們無法準確定義它。
|
||||
|
||||

|
||||
|
||||
> [照片](https://unsplash.com/photos/75715CVEJhI)由 [Amber Kipp](https://unsplash.com/@sadmax) 提供,來自 Unsplash
|
||||
|
||||
為了看出“智能”這個術語的模糊性,試著回答這個問題:“貓是智能的嗎?”不同的人往往會給出不同的答案,因為沒有普遍接受的測試來證明這個說法是對還是錯。如果你認為有——試著讓你的貓參加智商測試吧……
|
||||
|
||||
✅ 花點時間思考你如何定義智能。一隻能解迷宮並獲得食物的烏鴉是智能的嗎?一個小孩是智能的嗎?
|
||||
|
||||
---
|
||||
|
||||
在討論 AGI 時,我們需要某種方法來判斷是否創造了一個真正智能的系統。[Alan Turing](https://en.wikipedia.org/wiki/Alan_Turing) 提出了一種方法,稱為**[圖靈測試](https://en.wikipedia.org/wiki/Turing_test)**,它也可以作為智能的定義。該測試將給定系統與某些本質上智能的事物(如真人)進行比較,並且因為任何自動化比較都可能被電腦程序繞過,我們使用人類審問者。如果人類無法在基於文字的對話中區分真人和電腦系統,那麼該系統就被認為是智能的。
|
||||
|
||||
> 一個名為 [Eugene Goostman](https://en.wikipedia.org/wiki/Eugene_Goostman) 的聊天機器人於2014年在聖彼得堡接近通過圖靈測試,使用了一個巧妙的個性技巧。它事先聲明自己是一個13歲的烏克蘭男孩,這解釋了知識的缺乏和文本中的一些不一致之處。該機器人在5分鐘的對話中說服了30%的評審員相信它是人類,這是一個圖靈認為機器能在2000年之前通過的指標。然而,應該理解的是,這並不表示我們已經創造了一個智能系統,或者電腦系統欺騙了人類審問者——系統並沒有欺騙人類,而是機器人創造者欺騙了人類!
|
||||
|
||||
✅ 你是否曾被聊天機器人欺騙,認為自己在與人類交談?它是如何說服你的?
|
||||
|
||||
## 人工智慧的不同方法
|
||||
|
||||
如果我們希望電腦像人類一樣行事,我們需要以某種方式在電腦內部模擬我們的思維方式。因此,我們需要嘗試理解什麼使人類具有智能。
|
||||
|
||||
> 為了能夠將智能編程到機器中,我們需要理解自己做決策的過程是如何運作的。如果你進行一些自我反思,你會意識到有些過程是潛意識進行的——例如,我們可以在不思考的情況下區分貓和狗——而另一些則涉及推理。
|
||||
|
||||
解決這個問題有兩種可能的方法:
|
||||
|
||||
自上而下方法(符號推理) | 自下而上方法(神經網路)
|
||||
---------------------------------------|-------------------------------------
|
||||
自上而下方法模擬人類解決問題的推理方式。它涉及從人類中提取**知識**,並以電腦可讀的形式表示。我們還需要開發一種在電腦內部模擬**推理**的方法。|自下而上方法模擬人類大腦的結構,由大量簡單單元組成,稱為**神經元**。每個神經元的作用類似於其輸入的加權平均值,我們可以通過提供**訓練數據**來訓練神經元網路以解決有用的問題。
|
||||
|
||||
此外,還有一些其他可能的智能方法:
|
||||
|
||||
* **湧現式**、**協同式**或**多代理方法**基於這樣的事實,即通過大量簡單代理的交互可以獲得複雜的智能行為。根據[進化控制論](https://en.wikipedia.org/wiki/Global_brain#Evolutionary_cybernetics),智能可以在*系統轉變*過程中從更簡單的反應行為中*湧現*。
|
||||
|
||||
* **進化方法**或**遺傳算法**是一種基於進化原則的優化過程。
|
||||
|
||||
我們將在課程後續考慮這些方法,但現在我們將重點放在兩個主要方向:自上而下和自下而上。
|
||||
|
||||
### 自上而下方法
|
||||
|
||||
在**自上而下方法**中,我們嘗試模擬我們的推理。因為我們可以在推理時跟隨自己的思維,我們可以嘗試將這個過程形式化並編程到電腦中。這被稱為**符號推理**。
|
||||
|
||||
人們往往在腦海中有一些指導其決策過程的規則。例如,當醫生診斷病人時,他或她可能意識到病人有發燒症狀,因此可能身體內部有炎症。通過將大量規則應用於特定問題,醫生可能能夠得出最終診斷。
|
||||
|
||||
這種方法高度依賴於**知識表示**和**推理**。從人類專家中提取知識可能是最困難的部分,因為醫生在許多情況下可能不知道自己為什麼得出特定診斷。有時解決方案只是自然而然地出現在他的腦海中,沒有明確的思考。有些任務,例如從照片中判斷一個人的年齡,根本無法簡化為知識操作。
|
||||
|
||||
### 自下而上方法
|
||||
|
||||
或者,我們可以嘗試模擬我們大腦中的最簡單元素——神經元。我們可以在電腦中構建所謂的**人工神經網路**,然後通過提供例子來教它解決問題。這個過程類似於新生兒通過觀察學習周圍環境的方式。
|
||||
|
||||
✅ 研究一下嬰兒是如何學習的。嬰兒大腦的基本元素是什麼?
|
||||
|
||||
> | 那機器學習呢? | |
|
||||
> |--------------|-----------|
|
||||
> | 基於電腦通過某些數據學習解決問題的人工智慧部分稱為**機器學習**。我們不會在本課程中考慮傳統機器學習——我們推薦您參考單獨的 [機器學習初學者課程](http://aka.ms/ml-beginners)。 |  |
|
||||
|
||||
## 人工智慧的簡史
|
||||
|
||||
人工智慧作為一個領域始於20世紀中期。最初,符號推理是主要方法,並取得了一些重要成功,例如專家系統——能夠在某些有限問題領域中充當專家的電腦程序。然而,很快就發現這種方法的可擴展性不佳。從專家中提取知識、在電腦中表示知識並保持知識庫的準確性,事實證明是一項非常複雜且在許多情況下成本過高的任務。這導致了1970年代的所謂[人工智慧寒冬](https://en.wikipedia.org/wiki/AI_winter)。
|
||||
|
||||
<img alt="人工智慧簡史" src="../../../../translated_images/zh-TW/history-of-ai.7e83efa70b537f5a.webp" width="70%"/>
|
||||
|
||||
> 圖片由 [Dmitry Soshnikov](http://soshnikov.com) 提供
|
||||
|
||||
隨著時間的推移,計算資源變得更便宜,更多數據可用,神經網路方法開始在許多領域(如計算機視覺或語音理解)中展現出色的表現。在過去十年中,“人工智慧”這個術語大多被用作神經網路的同義詞,因為我們聽到的大多數人工智慧成功案例都基於神經網路。
|
||||
|
||||
我們可以觀察到方法的變化,例如在創建下棋電腦程序方面:
|
||||
|
||||
* 早期的下棋程序基於搜索——程序明確嘗試估算對手在一定步數內的可能走法,並根據幾步內可以達到的最佳位置選擇最佳走法。這導致了所謂的[alpha-beta剪枝](https://en.wikipedia.org/wiki/Alpha%E2%80%93beta_pruning)搜索算法的發展。
|
||||
* 搜索策略在遊戲結束時效果很好,因為搜索空間受到少量可能走法的限制。然而,在遊戲開始時,搜索空間巨大,算法可以通過學習人類玩家之間的現有比賽來改進。隨後的實驗採用了所謂的[基於案例推理](https://en.wikipedia.org/wiki/Case-based_reasoning),程序在知識庫中尋找與當前棋局非常相似的案例。
|
||||
* 現代能夠擊敗人類玩家的程序基於神經網路和[強化學習](https://en.wikipedia.org/wiki/Reinforcement_learning),程序通過長時間與自己對弈並從自己的錯誤中學習來學會下棋——就像人類學習下棋一樣。然而,電腦程序可以在更短的時間內玩更多的棋局,因此可以學得更快。
|
||||
|
||||
✅ 研究一下其他由人工智慧玩過的遊戲。
|
||||
|
||||
同樣,我們可以看到創建“會說話的程序”(可能通過圖靈測試)的方法如何改變:
|
||||
|
||||
* 早期的此類程序,例如 [Eliza](https://en.wikipedia.org/wiki/ELIZA),基於非常簡單的語法規則和將輸入句子重新表述為問題。
|
||||
* 現代助理,例如 Cortana、Siri 或 Google Assistant,都是混合系統,使用神經網路將語音轉換為文本並識別我們的意圖,然後採用一些推理或明確算法執行所需操作。
|
||||
* 未來,我們可能期待一個完全基於神經網路的模型能夠自行處理對話。最近的 GPT 和 [Turing-NLG](https://www.microsoft.com/research/blog/turing-nlg-a-17-billion-parameter-language-model-by-microsoft) 神經網路系列顯示了巨大的成功。
|
||||
|
||||
<img alt="圖靈測試的演變" src="../../../../translated_images/zh-TW/turing-test-evol.4184696701293ead.webp" width="70%"/>
|
||||
> 圖片由 Dmitry Soshnikov 提供,[照片](https://unsplash.com/photos/r8LmVbUKgns) 由 [Marina Abrosimova](https://unsplash.com/@abrosimova_marina_foto) 提供,Unsplash
|
||||
|
||||
## 最近的人工智慧研究
|
||||
|
||||
神經網路研究的快速增長始於 2010 年左右,當時大型公共數據集開始變得可用。一個名為 [ImageNet](https://en.wikipedia.org/wiki/ImageNet) 的龐大圖像集合,包含約 1400 萬張帶有註解的圖像,催生了 [ImageNet 大規模視覺識別挑戰賽](https://image-net.org/challenges/LSVRC/)。
|
||||
|
||||

|
||||
|
||||
> 圖片由 [Dmitry Soshnikov](http://soshnikov.com) 提供
|
||||
|
||||
2012 年,[卷積神經網路](../4-ComputerVision/07-ConvNets/README.md) 首次被用於圖像分類,這導致分類錯誤率顯著下降(從接近 30% 降至 16.4%)。2015 年,微軟研究院的 ResNet 架構[達到了人類水準的準確率](https://doi.org/10.1109/ICCV.2015.123)。
|
||||
|
||||
自那時起,神經網路在許多任務中展現了非常成功的表現:
|
||||
|
||||
---
|
||||
|
||||
年份 | 達到人類水準的領域
|
||||
-----|--------
|
||||
2015 | [圖像分類](https://doi.org/10.1109/ICCV.2015.123)
|
||||
2016 | [對話式語音識別](https://arxiv.org/abs/1610.05256)
|
||||
2018 | [自動機器翻譯](https://arxiv.org/abs/1803.05567)(中翻英)
|
||||
2020 | [圖像標註](https://arxiv.org/abs/2009.13682)
|
||||
|
||||
在過去幾年中,我們見證了大型語言模型的巨大成功,例如 BERT 和 GPT-3。這主要是因為有大量的通用文本數據可用,這使我們能夠訓練模型來捕捉文本的結構和意義,先在通用文本集合上進行預訓練,然後再將這些模型專門化用於更具體的任務。我們將在本課程稍後的[自然語言處理](../5-NLP/README.md)部分中學到更多。
|
||||
|
||||
## 🚀 挑戰
|
||||
|
||||
在網路上進行探索,判斷您認為人工智慧在哪些領域最有效地被使用。是地圖應用程式、語音轉文字服務,還是某款電子遊戲?研究該系統是如何構建的。
|
||||
|
||||
## [課後測驗](https://ff-quizzes.netlify.app/en/ai/quiz/2)
|
||||
|
||||
## 回顧與自學
|
||||
|
||||
透過閱讀[這一課程](https://github.com/microsoft/ML-For-Beginners/tree/main/1-Introduction/2-history-of-ML)來回顧人工智慧和機器學習的歷史。從該課程或本課程頂部的手繪筆記中選擇一個元素,深入研究其文化背景,以了解其演變過程。
|
||||
|
||||
**作業**:[遊戲開發馬拉松](assignment.md)
|
||||
|
||||
---
|
||||
|
||||
<!-- CO-OP TRANSLATOR DISCLAIMER START -->
|
||||
**免責聲明**:
|
||||
本文件使用 AI 翻譯服務 [Co-op Translator](https://github.com/Azure/co-op-translator) 進行翻譯。雖然我們致力於提供準確的翻譯,但請注意,自動翻譯可能包含錯誤或不準確之處。原始文件的母語版本應被視為權威來源。對於關鍵資訊,建議使用專業人工翻譯。我們對因使用此翻譯而產生的任何誤解或誤釋不承擔責任。
|
||||
<!-- CO-OP TRANSLATOR DISCLAIMER END -->
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
# 遊戲創作馬拉松
|
||||
|
||||
遊戲是一個深受人工智慧(AI)和機器學習(ML)發展影響的領域。在這次作業中,請撰寫一篇短文,討論一款你喜歡的遊戲,並說明它如何受到AI演進的影響。這款遊戲應該有足夠的歷史,能反映出多種電腦處理系統的影響。一個不錯的例子是象棋或圍棋,但你也可以考慮像乒乓球(Pong)或吃豆人(Pac-Man)這樣的電子遊戲。撰寫一篇文章,探討該遊戲的過去、現在以及AI的未來發展。
|
||||
|
||||
---
|
||||
|
||||
<!-- CO-OP TRANSLATOR DISCLAIMER START -->
|
||||
**免責聲明**:
|
||||
本文件使用 AI 翻譯服務 [Co-op Translator](https://github.com/Azure/co-op-translator) 進行翻譯。雖然我們致力於提供準確的翻譯,但請注意,自動翻譯可能包含錯誤或不準確之處。原始文件的母語版本應被視為權威來源。對於關鍵資訊,建議使用專業人工翻譯。我們對因使用此翻譯而產生的任何誤解或誤釋不承擔責任。
|
||||
<!-- CO-OP TRANSLATOR DISCLAIMER END -->
|
||||
|
|
@ -0,0 +1,477 @@
|
|||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"collapsed": true
|
||||
},
|
||||
"source": [
|
||||
"# 實作一個動物專家系統\n",
|
||||
"\n",
|
||||
"一個來自 [AI for Beginners Curriculum](http://github.com/microsoft/ai-for-beginners) 的範例。\n",
|
||||
"\n",
|
||||
"在此範例中,我們將實作一個簡單的知識基礎系統,根據一些物理特徵來判斷動物。系統可以用以下的 AND-OR 樹來表示(這是整個樹的一部分,我們可以輕鬆地添加更多規則):\n",
|
||||
"\n",
|
||||
"\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## 我們自己的專家系統外殼與逆向推理\n",
|
||||
"\n",
|
||||
"讓我們嘗試定義一個基於產生規則的簡單知識表示語言。我們將使用 Python 類別作為關鍵字來定義規則。基本上會有 3 種主要的類別:\n",
|
||||
"* `Ask` 代表需要向使用者提問的問題。它包含可能的答案集合。\n",
|
||||
"* `If` 代表一條規則,它只是用語法糖來儲存規則的內容。\n",
|
||||
"* `AND`/`OR` 是用來表示樹狀結構中 AND/OR 分支的類別。它們只儲存內部的引數列表。為了簡化程式碼,所有功能皆定義在父類別 `Content` 裡。\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 1,
|
||||
"metadata": {
|
||||
"trusted": true
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"class Ask():\n",
|
||||
" def __init__(self,choices=['y','n']):\n",
|
||||
" self.choices = choices\n",
|
||||
" def ask(self):\n",
|
||||
" if max([len(x) for x in self.choices])>1:\n",
|
||||
" for i,x in enumerate(self.choices):\n",
|
||||
" print(\"{0}. {1}\".format(i,x),flush=True)\n",
|
||||
" x = int(input())\n",
|
||||
" return self.choices[x]\n",
|
||||
" else:\n",
|
||||
" print(\"/\".join(self.choices),flush=True)\n",
|
||||
" return input()\n",
|
||||
"\n",
|
||||
"class Content():\n",
|
||||
" def __init__(self,x):\n",
|
||||
" self.x=x\n",
|
||||
" \n",
|
||||
"class If(Content):\n",
|
||||
" pass\n",
|
||||
"\n",
|
||||
"class AND(Content):\n",
|
||||
" pass\n",
|
||||
"\n",
|
||||
"class OR(Content):\n",
|
||||
" pass"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"在我們的系統中,工作記憶會包含作為**屬性-值對**的**事實**列表。知識庫可以定義為一個大型字典,將動作(應該插入工作記憶的新事實)對應到以 AND-OR 表達式表示的條件。此外,有些事實可以被`Ask`詢問。\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 2,
|
||||
"metadata": {
|
||||
"trusted": true
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"rules = {\n",
|
||||
" 'default': Ask(['y','n']),\n",
|
||||
" 'color' : Ask(['red-brown','black and white','other']),\n",
|
||||
" 'pattern' : Ask(['dark stripes','dark spots']),\n",
|
||||
" 'mammal': If(OR(['hair','gives milk'])),\n",
|
||||
" 'carnivor': If(OR([AND(['sharp teeth','claws','forward-looking eyes']),'eats meat'])),\n",
|
||||
" 'ungulate': If(['mammal',OR(['has hooves','chews cud'])]),\n",
|
||||
" 'bird': If(OR(['feathers',AND(['flies','lies eggs'])])),\n",
|
||||
" 'animal:monkey' : If(['mammal','carnivor','color:red-brown','pattern:dark spots']),\n",
|
||||
" 'animal:tiger' : If(['mammal','carnivor','color:red-brown','pattern:dark stripes']),\n",
|
||||
" 'animal:giraffe' : If(['ungulate','long neck','long legs','pattern:dark spots']),\n",
|
||||
" 'animal:zebra' : If(['ungulate','pattern:dark stripes']),\n",
|
||||
" 'animal:ostrich' : If(['bird','long nech','color:black and white','cannot fly']),\n",
|
||||
" 'animal:pinguin' : If(['bird','swims','color:black and white','cannot fly']),\n",
|
||||
" 'animal:albatross' : If(['bird','flies well'])\n",
|
||||
"}"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"為了執行反向推理,我們將定義 `Knowledgebase` 類別。它將包含:\n",
|
||||
"* 工作記憶體 `memory` - 一個將屬性映射到值的字典\n",
|
||||
"* 知識庫 `rules`,格式如上所述定義\n",
|
||||
"\n",
|
||||
"兩個主要方法是:\n",
|
||||
"* `get` 用於取得屬性的值,必要時執行推理。例如,`get('color')` 將取得一個顏色欄位的值(必要時會詢問,並將值存入工作記憶體以便日後使用)。如果我們詢問 `get('color:blue')`,它會詢問顏色,然後根據顏色回傳 `y`/`n` 值。\n",
|
||||
"* `eval` 執行實際推理,即遍歷 AND/OR 樹,評估子任務,等等。\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 3,
|
||||
"metadata": {
|
||||
"trusted": true
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"class KnowledgeBase():\n",
|
||||
" def __init__(self,rules):\n",
|
||||
" self.rules = rules\n",
|
||||
" self.memory = {}\n",
|
||||
" \n",
|
||||
" def get(self,name):\n",
|
||||
" if ':' in name:\n",
|
||||
" k,v = name.split(':')\n",
|
||||
" vv = self.get(k)\n",
|
||||
" return 'y' if v==vv else 'n'\n",
|
||||
" if name in self.memory.keys():\n",
|
||||
" return self.memory[name]\n",
|
||||
" for fld in self.rules.keys():\n",
|
||||
" if fld==name or fld.startswith(name+\":\"):\n",
|
||||
" # print(\" + proving {}\".format(fld))\n",
|
||||
" value = 'y' if fld==name else fld.split(':')[1]\n",
|
||||
" res = self.eval(self.rules[fld],field=name)\n",
|
||||
" if res!='y' and res!='n' and value=='y':\n",
|
||||
" self.memory[name] = res\n",
|
||||
" return res\n",
|
||||
" if res=='y':\n",
|
||||
" self.memory[name] = value\n",
|
||||
" return value\n",
|
||||
" # field is not found, using default\n",
|
||||
" res = self.eval(self.rules['default'],field=name)\n",
|
||||
" self.memory[name]=res\n",
|
||||
" return res\n",
|
||||
" \n",
|
||||
" def eval(self,expr,field=None):\n",
|
||||
" # print(\" + eval {}\".format(expr))\n",
|
||||
" if isinstance(expr,Ask):\n",
|
||||
" print(field)\n",
|
||||
" return expr.ask()\n",
|
||||
" elif isinstance(expr,If):\n",
|
||||
" return self.eval(expr.x)\n",
|
||||
" elif isinstance(expr,AND) or isinstance(expr,list):\n",
|
||||
" expr = expr.x if isinstance(expr,AND) else expr\n",
|
||||
" for x in expr:\n",
|
||||
" if self.eval(x)=='n':\n",
|
||||
" return 'n'\n",
|
||||
" return 'y'\n",
|
||||
" elif isinstance(expr,OR):\n",
|
||||
" for x in expr.x:\n",
|
||||
" if self.eval(x)=='y':\n",
|
||||
" return 'y'\n",
|
||||
" return 'n'\n",
|
||||
" elif isinstance(expr,str):\n",
|
||||
" return self.get(expr)\n",
|
||||
" else:\n",
|
||||
" print(\"Unknown expr: {}\".format(expr))"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"現在讓我們定義我們的動物知識庫並進行諮詢。請注意,此呼叫將會問您問題。您可以對是非題輸入 `y`/`n` 作答,或者對較長的多選題指定數字(0..N)進行回答。\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 4,
|
||||
"metadata": {
|
||||
"trusted": true
|
||||
},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"hair\n",
|
||||
"y/n\n",
|
||||
"sharp teeth\n",
|
||||
"y/n\n",
|
||||
"claws\n",
|
||||
"y/n\n",
|
||||
"forward-looking eyes\n",
|
||||
"y/n\n",
|
||||
"color\n",
|
||||
"0. red-brown\n",
|
||||
"1. black and white\n",
|
||||
"2. other\n",
|
||||
"has hooves\n",
|
||||
"y/n\n",
|
||||
"long neck\n",
|
||||
"y/n\n",
|
||||
"long legs\n",
|
||||
"y/n\n",
|
||||
"pattern\n",
|
||||
"0. dark stripes\n",
|
||||
"1. dark spots\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"'giraffe'"
|
||||
]
|
||||
},
|
||||
"execution_count": 4,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"kb = KnowledgeBase(rules)\n",
|
||||
"kb.get('animal')"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## 使用 Experta 進行正向推論\n",
|
||||
"\n",
|
||||
"在下一個範例中,我們將嘗試使用其中一個知識表示庫 [Experta](https://github.com/nilp0inter/experta) 來實作正向推論。**Experta** 是一個用於在 Python 中建立正向推論系統的庫,其設計類似於經典的舊系統 [CLIPS](http://www.clipsrules.net/index.html)。\n",
|
||||
"\n",
|
||||
"我們當然也可以自己實作正向鏈結,但天真的實作通常效率不高。為了更有效率的規則匹配,使用了一種特殊的演算法 [Rete](https://en.wikipedia.org/wiki/Rete_algorithm)。\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 5,
|
||||
"metadata": {
|
||||
"trusted": true
|
||||
},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"Collecting git+https://github.com/nilp0inter/experta\n",
|
||||
" Cloning https://github.com/nilp0inter/experta to /tmp/pip-req-build-7qurtwk3\n",
|
||||
" Running command git clone --filter=blob:none --quiet https://github.com/nilp0inter/experta /tmp/pip-req-build-7qurtwk3\n",
|
||||
" Resolved https://github.com/nilp0inter/experta to commit c6d5834b123861f5ae09e7d07027dc98bec58741\n",
|
||||
" Installing build dependencies ... \u001b[?25ldone\n",
|
||||
"\u001b[?25h Getting requirements to build wheel ... \u001b[?25ldone\n",
|
||||
"\u001b[?25h Preparing metadata (pyproject.toml) ... \u001b[?25ldone\n",
|
||||
"\u001b[?25hRequirement already satisfied: frozendict~=2.4.6 in /opt/conda/envs/ai4beg/lib/python3.12/site-packages (from experta==1.9.5.dev1) (2.4.7)\n",
|
||||
"Collecting schema~=0.6.7 (from experta==1.9.5.dev1)\n",
|
||||
" Downloading schema-0.6.8-py2.py3-none-any.whl.metadata (14 kB)\n",
|
||||
"Downloading schema-0.6.8-py2.py3-none-any.whl (14 kB)\n",
|
||||
"Building wheels for collected packages: experta\n",
|
||||
" Building wheel for experta (pyproject.toml) ... \u001b[?25ldone\n",
|
||||
"\u001b[?25h Created wheel for experta: filename=experta-1.9.5.dev1-py3-none-any.whl size=34804 sha256=888c459512a5e713f4b674caa9a0f96cfdf07ec0d6eb56cc318ce0653d218014\n",
|
||||
" Stored in directory: /tmp/pip-ephem-wheel-cache-1eeii9zy/wheels/3d/e8/bb/22d7956359603fa8dd679aa09f5b8efb3f29991c3986fdc787\n",
|
||||
"Successfully built experta\n",
|
||||
"Installing collected packages: schema, experta\n",
|
||||
"\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m2/2\u001b[0m [experta]\n",
|
||||
"\u001b[1A\u001b[2KSuccessfully installed experta-1.9.5.dev1 schema-0.6.8\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"import sys\n",
|
||||
"!{sys.executable} -m pip install git+https://github.com/nilp0inter/experta"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 13,
|
||||
"metadata": {
|
||||
"trusted": true
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from experta import *\n",
|
||||
"#import experta"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"我們將系統定義為繼承自 `KnowledgeEngine` 的類別。每個規則由帶有 `@Rule` 註解的單獨函式定義,該註解指定規則何時觸發。在規則內部,我們可以使用 `declare` 函式新增新的事實,新增這些事實將導致前向推理引擎調用更多規則。\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 14,
|
||||
"metadata": {
|
||||
"trusted": true
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"class Animals(KnowledgeEngine):\n",
|
||||
" @Rule(OR(\n",
|
||||
" AND(Fact('sharp teeth'),Fact('claws'),Fact('forward looking eyes')),\n",
|
||||
" Fact('eats meat')))\n",
|
||||
" def cornivor(self):\n",
|
||||
" self.declare(Fact('carnivor'))\n",
|
||||
" \n",
|
||||
" @Rule(OR(Fact('hair'),Fact('gives milk')))\n",
|
||||
" def mammal(self):\n",
|
||||
" self.declare(Fact('mammal'))\n",
|
||||
"\n",
|
||||
" @Rule(Fact('mammal'),\n",
|
||||
" OR(Fact('has hooves'),Fact('chews cud')))\n",
|
||||
" def hooves(self):\n",
|
||||
" self.declare('ungulate')\n",
|
||||
" \n",
|
||||
" @Rule(OR(Fact('feathers'),AND(Fact('flies'),Fact('lays eggs'))))\n",
|
||||
" def bird(self):\n",
|
||||
" self.declare('bird')\n",
|
||||
" \n",
|
||||
" @Rule(Fact('mammal'),Fact('carnivor'),\n",
|
||||
" Fact(color='red-brown'),\n",
|
||||
" Fact(pattern='dark spots'))\n",
|
||||
" def monkey(self):\n",
|
||||
" self.declare(Fact(animal='monkey'))\n",
|
||||
"\n",
|
||||
" @Rule(Fact('mammal'),Fact('carnivor'),\n",
|
||||
" Fact(color='red-brown'),\n",
|
||||
" Fact(pattern='dark stripes'))\n",
|
||||
" def tiger(self):\n",
|
||||
" self.declare(Fact(animal='tiger'))\n",
|
||||
"\n",
|
||||
" @Rule(Fact('ungulate'),\n",
|
||||
" Fact('long neck'),\n",
|
||||
" Fact('long legs'),\n",
|
||||
" Fact(pattern='dark spots'))\n",
|
||||
" def giraffe(self):\n",
|
||||
" self.declare(Fact(animal='giraffe'))\n",
|
||||
"\n",
|
||||
" @Rule(Fact('ungulate'),\n",
|
||||
" Fact(pattern='dark stripes'))\n",
|
||||
" def zebra(self):\n",
|
||||
" self.declare(Fact(animal='zebra'))\n",
|
||||
"\n",
|
||||
" @Rule(Fact('bird'),\n",
|
||||
" Fact('long neck'),\n",
|
||||
" Fact('cannot fly'),\n",
|
||||
" Fact(color='black and white'))\n",
|
||||
" def straus(self):\n",
|
||||
" self.declare(Fact(animal='ostrich'))\n",
|
||||
"\n",
|
||||
" @Rule(Fact('bird'),\n",
|
||||
" Fact('swims'),\n",
|
||||
" Fact('cannot fly'),\n",
|
||||
" Fact(color='black and white'))\n",
|
||||
" def pinguin(self):\n",
|
||||
" self.declare(Fact(animal='pinguin'))\n",
|
||||
"\n",
|
||||
" @Rule(Fact('bird'),\n",
|
||||
" Fact('flies well'))\n",
|
||||
" def albatros(self):\n",
|
||||
" self.declare(Fact(animal='albatross'))\n",
|
||||
" \n",
|
||||
" @Rule(Fact(animal=MATCH.a))\n",
|
||||
" def print_result(self,a):\n",
|
||||
" print('Animal is {}'.format(a))\n",
|
||||
" \n",
|
||||
" def factz(self,l):\n",
|
||||
" for x in l:\n",
|
||||
" self.declare(x)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"一旦我們定義了知識庫,我們會將一些初始事實填入工作記憶,然後呼叫 `run()` 方法來執行推理。你可以看到作為結果,新的推論事實會被加入到工作記憶中,包括關於動物的最終事實(如果我們正確設定了所有初始事實)。\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 15,
|
||||
"metadata": {
|
||||
"trusted": true
|
||||
},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"Animal is tiger\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"FactList([(0, InitialFact()),\n",
|
||||
" (1, Fact(color='red-brown')),\n",
|
||||
" (2, Fact(pattern='dark stripes')),\n",
|
||||
" (3, Fact('sharp teeth')),\n",
|
||||
" (4, Fact('claws')),\n",
|
||||
" (5, Fact('forward looking eyes')),\n",
|
||||
" (6, Fact('gives milk')),\n",
|
||||
" (7, Fact('mammal')),\n",
|
||||
" (8, Fact('carnivor')),\n",
|
||||
" (9, Fact(animal='tiger'))])"
|
||||
]
|
||||
},
|
||||
"execution_count": 15,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"ex1 = Animals()\n",
|
||||
"ex1.reset()\n",
|
||||
"ex1.factz([\n",
|
||||
" Fact(color='red-brown'),\n",
|
||||
" Fact(pattern='dark stripes'),\n",
|
||||
" Fact('sharp teeth'),\n",
|
||||
" Fact('claws'),\n",
|
||||
" Fact('forward looking eyes'),\n",
|
||||
" Fact('gives milk')])\n",
|
||||
"ex1.run()\n",
|
||||
"ex1.facts"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": []
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"---\n\n<!-- CO-OP TRANSLATOR DISCLAIMER START -->\n**免責聲明**:\n本文件係使用 AI 翻譯服務 [Co-op Translator](https://github.com/Azure/co-op-translator) 進行翻譯。雖然我們致力於確保準確性,但請注意,機器翻譯可能存在錯誤或不準確之處。原始文件的母語版本應視為權威來源。對於重要資訊,建議採用專業人工翻譯。我們不對因使用本翻譯所產生的任何誤解或誤譯承擔責任。\n<!-- CO-OP TRANSLATOR DISCLAIMER END -->\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": "8ef43db4b9182239fd150a76bd494fdb",
|
||||
"translation_date": "2026-01-15T11:44:31+00:00",
|
||||
"source_file": "lessons/2-Symbolic/Animals.ipynb",
|
||||
"language_code": "tw"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 2
|
||||
}
|
||||
|
|
@ -0,0 +1,595 @@
|
|||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"collapsed": true
|
||||
},
|
||||
"source": [
|
||||
"# 家庭關係本體\n",
|
||||
"\n",
|
||||
"此範例是 [AI for Beginners Curriculum](http://github.com/microsoft/ai-for-beginners) 的一部分,靈感來自於[這篇部落格文章](https://habr.com/post/270857/)。\n",
|
||||
"\n",
|
||||
"我總是覺得記住家庭中人與人之間的不同關係很困難。在這個範例中,我們將使用一個定義家庭關係的本體,以及實際的家譜樹,來展示如何進行自動推理以找出所有親屬。\n",
|
||||
"\n",
|
||||
"### 獲取家譜樹\n",
|
||||
"\n",
|
||||
"作為範例,我們將使用[羅曼諾夫沙皇家族](https://en.wikipedia.org/wiki/House_of_Romanov)的家譜樹。描述家庭關係最常見的格式是 [GEDCOM](https://en.wikipedia.org/wiki/GEDCOM)。我們將使用 GEDCOM 格式的羅曼諾夫家族樹:\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 1,
|
||||
"metadata": {
|
||||
"trusted": true
|
||||
},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"0 HEAD\n",
|
||||
"1 CHAR UTF8\n",
|
||||
"1 GEDC\n",
|
||||
"2 VERS 5.5\n",
|
||||
"0 @0@ INDI\n",
|
||||
"1 NAME Mihail Fedorovich /Romanov/\n",
|
||||
"1 SEX M\n",
|
||||
"1 BIRT\n",
|
||||
"2 DATE 1613\n",
|
||||
"1 DEAT \n",
|
||||
"2 DATE 1645\n",
|
||||
"1 FAMS @41@\n",
|
||||
"0 @1@ INDI\n",
|
||||
"1 NAME Evdokija Lukjanovna /Streshneva/\n",
|
||||
"1 SEX F\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"!head -15 data/tsars.ged"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"要使用 GEDCOM 文件,我們可以使用 `python-gedcom` 庫:\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 2,
|
||||
"metadata": {
|
||||
"trusted": true
|
||||
},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"Collecting python-gedcom\n",
|
||||
" Downloading python_gedcom-1.0.0-py2.py3-none-any.whl (35 kB)\n",
|
||||
"Installing collected packages: python-gedcom\n",
|
||||
"Successfully installed python-gedcom-1.0.0\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"import sys\n",
|
||||
"!{sys.executable} -m pip install python-gedcom"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"此程式庫解決了一些與檔案解析相關的技術問題,但仍然提供了對樹中所有個人和家庭的相當低層次的訪問。以下是我們如何解析檔案並顯示所有個人列表的方法:\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 3,
|
||||
"metadata": {
|
||||
"trusted": true
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from gedcom.parser import Parser\n",
|
||||
"from gedcom.element.individual import IndividualElement\n",
|
||||
"from gedcom.element.family import FamilyElement\n",
|
||||
"g = Parser()\n",
|
||||
"g.parse_file('data/tsars.ged')"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 4,
|
||||
"metadata": {
|
||||
"scrolled": true,
|
||||
"trusted": true
|
||||
},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"[('@0@', ('Mihail Fedorovich', 'Romanov')),\n",
|
||||
" ('@1@', ('Evdokija Lukjanovna', 'Streshneva')),\n",
|
||||
" ('@2@', ('Aleksej Mihajlovich', 'Romanov')),\n",
|
||||
" ('@3@', ('Marija Ilinichna', 'Miloslavskaja')),\n",
|
||||
" ('@4@', ('Natalja Kirillovna', 'Naryshkina')),\n",
|
||||
" ('@5@', ('Marfa Matveevna', 'Apraksina')),\n",
|
||||
" ('@6@', ('Fedor Alekseevich', 'Romanov')),\n",
|
||||
" ('@7@', ('Sofja Aleksevna', 'Romanova')),\n",
|
||||
" ('@8@', ('Ivan V Alekseevich', 'Romanov')),\n",
|
||||
" ('@9@', ('Praskovja Fedorovna', 'Saltykova')),\n",
|
||||
" ('@10@', ('Ekaterina Ivanovna', 'Romanova')),\n",
|
||||
" ('@11@', ('Anna Ivanovna', 'Romanova')),\n",
|
||||
" ('@12@', ('Fridrih Vilgelm', 'Kurlandskij')),\n",
|
||||
" ('@13@', ('Karl Leopold', 'Meklenburg-Shverinskij')),\n",
|
||||
" ('@14@', ('Anna Leopoldovna', 'Meklenburg-Shverinskaja')),\n",
|
||||
" ('@15@', ('Anton Ulrih', 'Braunshvejg-Volfenbjuttelskij')),\n",
|
||||
" ('@16@', ('Ivan VI Antonovich', 'Braunshvejg-Volfenbjuttelskij')),\n",
|
||||
" ('@17@', ('Petr I Alekseevich', 'Romanov')),\n",
|
||||
" ('@18@', ('Evdokija Fedorovna', 'Lopuhina')),\n",
|
||||
" ('@19@', ('Ekaterina I Alekseevna', 'Mihajlova')),\n",
|
||||
" ('@20@', ('Aleksej Petrovich', 'Romanov')),\n",
|
||||
" ('@21@', ('Sharlotta Kristina', 'Braunshvejg-Volfenbjuttelskaja')),\n",
|
||||
" ('@22@', ('Petr II Alekseevich', 'Romanov')),\n",
|
||||
" ('@23@', ('Anna Petrovna', 'Romanova')),\n",
|
||||
" ('@24@', ('Elizaveta Petrovna', 'Romanova')),\n",
|
||||
" ('@25@', ('Karl Fridrih', 'Golshtejn-Gottorpskij')),\n",
|
||||
" ('@26@', ('Petr III Fedorovich', 'Romanov')),\n",
|
||||
" ('@27@', ('Ekaterina II', 'Alekseevna')),\n",
|
||||
" ('@28@', ('Pavel I Petrovich', 'Romanov')),\n",
|
||||
" ('@29@', ('Natalja Alekseevna', 'Gessen-Darmshtadskaja')),\n",
|
||||
" ('@30@', ('Marija Fedorovna', 'Vjurtembergskaja')),\n",
|
||||
" ('@31@', ('Aleksandr I Pavlovich', 'Romanov')),\n",
|
||||
" ('@32@', ('Elizaveta Alekseevna', 'Baden-Durlahskaja')),\n",
|
||||
" ('@33@', ('Nikolaj I Pavlovich', 'Romanov')),\n",
|
||||
" ('@34@', ('Aleksandra Fedorovna', 'Prusskaja')),\n",
|
||||
" ('@35@', ('Aleksandr II Nikolaevich', 'Romanov')),\n",
|
||||
" ('@36@', ('Marija Aleksandrovna', 'Gessenskaja')),\n",
|
||||
" ('@37@', ('Aleksandr III Aleksandrovich', 'Romanov')),\n",
|
||||
" ('@38@', ('Marija Fedorovna', 'Datskaja')),\n",
|
||||
" ('@39@', ('Nikolaj II Aleksandrovich', 'Romanov')),\n",
|
||||
" ('@40@', ('Aleksandra Fedorovna', 'Gessenskaja'))]"
|
||||
]
|
||||
},
|
||||
"execution_count": 4,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"d = g.get_element_dictionary()\n",
|
||||
"[ (k,v.get_name()) for k,v in d.items() if isinstance(v,IndividualElement)]"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"以下是我們如何獲取有關家庭的信息。請注意,這會給我們一個**標識符**的列表,如果我們想要更清楚的資訊,我們需要將它們轉換為名稱:\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 5,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"[('@41@', ['@0@', '@1@', '@2@']),\n",
|
||||
" ('@42@', ['@2@', '@3@', '@6@', '@7@', '@8@']),\n",
|
||||
" ('@43@', ['@8@', '@9@', '@10@', '@11@']),\n",
|
||||
" ('@44@', ['@13@', '@10@', '@14@']),\n",
|
||||
" ('@45@', ['@15@', '@14@', '@16@']),\n",
|
||||
" ('@46@', ['@2@', '@4@', '@17@']),\n",
|
||||
" ('@47@', ['@17@', '@18@', '@20@']),\n",
|
||||
" ('@48@', ['@20@', '@21@', '@22@']),\n",
|
||||
" ('@49@', ['@17@', '@19@', '@23@', '@24@']),\n",
|
||||
" ('@50@', ['@25@', '@23@', '@26@']),\n",
|
||||
" ('@51@', ['@26@', '@27@', '@28@']),\n",
|
||||
" ('@52@', ['@28@', '@30@', '@31@', '@33@']),\n",
|
||||
" ('@53@', ['@33@', '@34@', '@35@']),\n",
|
||||
" ('@54@', ['@35@', '@36@', '@37@']),\n",
|
||||
" ('@55@', ['@37@', '@38@', '@39@'])]"
|
||||
]
|
||||
},
|
||||
"execution_count": 5,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"d = g.get_element_dictionary()\n",
|
||||
"[ (k,[x.get_value() for x in v.get_child_elements()]) for k,v in d.items() if isinstance(v,FamilyElement)]"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"### 獲取家庭本體\n",
|
||||
"\n",
|
||||
"接下來,我們來看看[家庭本體](https://raw.githubusercontent.com/blokhin/genealogical-trees/master/data/header.ttl),它被定義為一組語義網三元組。這個本體定義了像 `isUncleOf`、`isCousinOf` 等許多關係。所有這些關係都是基於基本謂詞 `isMotherOf`、`isFatherOf`、`isBrotherOf` 和 `isSisterOf` 定義的。我們將使用自動推理來利用該本體推導出所有其他關係。\n",
|
||||
"\n",
|
||||
"以下是 `isAuntOf` 屬性的範例定義,它被定義為 `isSisterOf` 和 `isParentOf` 的組合(*姑姑或阿姨是某人父母的姐妹*)。\n",
|
||||
"\n",
|
||||
"```\n",
|
||||
"fhkb:isAuntOf a owl:ObjectProperty ;\n",
|
||||
" rdfs:domain fhkb:Woman ;\n",
|
||||
" rdfs:range fhkb:Person ;\n",
|
||||
" owl:propertyChainAxiom ( fhkb:isSisterOf fhkb:isParentOf ) .\n",
|
||||
"```\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 6,
|
||||
"metadata": {
|
||||
"trusted": true
|
||||
},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"@prefix fhkb: <http://www.example.com/genealogy.owl#> .\n",
|
||||
"@prefix owl: <http://www.w3.org/2002/07/owl#> .\n",
|
||||
"@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .\n",
|
||||
"@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .\n",
|
||||
"@prefix xml: <http://www.w3.org/XML/1998/namespace> .\n",
|
||||
"@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .\n",
|
||||
"\n",
|
||||
"<http://www.example.com/genealogy.owl#> a owl:Ontology .\n",
|
||||
"\n",
|
||||
"fhkb:DomainEntity a owl:Class .\n",
|
||||
"\n",
|
||||
"fhkb:Man a owl:Class ;\n",
|
||||
" owl:equivalentClass [ a owl:Class ;\n",
|
||||
" owl:intersectionOf ( fhkb:Person [ a owl:Restriction ;\n",
|
||||
" owl:onProperty fhkb:hasSex ;\n",
|
||||
" owl:someValuesFrom fhkb:Male ] ) ] .\n",
|
||||
"\n",
|
||||
"fhkb:Woman a owl:Class ;\n",
|
||||
" owl:equivalentClass [ a owl:Class ;\n",
|
||||
" owl:intersectionOf ( fhkb:Person [ a owl:Restriction ;\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"!head -20 data/onto.ttl"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"### 建構推理本體\n",
|
||||
"\n",
|
||||
"為了簡化,我們將建立一個本體文件,其中包含家庭本體的原始規則,以及來自我們 GEDCOM 文件的個人事實。我們將逐步處理 GEDCOM 文件,提取有關家庭和個人的信息,並將其轉換為三元組。\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 7,
|
||||
"metadata": {
|
||||
"trusted": true
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"!cp data/onto.ttl .\n",
|
||||
"\n",
|
||||
"gedcom_dict = g.get_element_dictionary()\n",
|
||||
"individuals, marriages = {}, {}\n",
|
||||
"\n",
|
||||
"def term2id(el):\n",
|
||||
" return \"i\" + el.get_pointer().replace('@', '').lower()\n",
|
||||
"\n",
|
||||
"out = open(\"onto.ttl\",\"a\")\n",
|
||||
"\n",
|
||||
"for k, v in gedcom_dict.items():\n",
|
||||
" if isinstance(v,IndividualElement):\n",
|
||||
" children, siblings = set(), set()\n",
|
||||
" idx = term2id(v)\n",
|
||||
"\n",
|
||||
" title = v.get_name()[0] + \" \" + v.get_name()[1]\n",
|
||||
" title = title.replace('\"', '').replace('[', '').replace(']', '').replace('(', '').replace(')', '').strip()\n",
|
||||
"\n",
|
||||
" own_families = g.get_families(v, 'FAMS')\n",
|
||||
" for fam in own_families:\n",
|
||||
" children |= set(term2id(i) for i in g.get_family_members(fam, \"CHIL\"))\n",
|
||||
"\n",
|
||||
" parent_families = g.get_families(v, 'FAMC')\n",
|
||||
" if len(parent_families):\n",
|
||||
" for member in g.get_family_members(parent_families[0], \"CHIL\"): # NB adoptive families i.e len(parent_families)>1 are not considered (TODO?)\n",
|
||||
" if member.get_pointer() == v.get_pointer():\n",
|
||||
" continue\n",
|
||||
" siblings.add(term2id(member))\n",
|
||||
"\n",
|
||||
" if idx in individuals:\n",
|
||||
" children |= individuals[idx].get('children', set())\n",
|
||||
" siblings |= individuals[idx].get('siblings', set())\n",
|
||||
" individuals[idx] = {'sex': v.get_gender().lower(), 'children': children, 'siblings': siblings, 'title': title}\n",
|
||||
"\n",
|
||||
" elif isinstance(v,FamilyElement):\n",
|
||||
" wife, husb, children = None, None, set()\n",
|
||||
" children = set(term2id(i) for i in g.get_family_members(v, \"CHIL\"))\n",
|
||||
"\n",
|
||||
" try:\n",
|
||||
" wife = g.get_family_members(v, \"WIFE\")[0]\n",
|
||||
" wife = term2id(wife)\n",
|
||||
" if wife in individuals: individuals[wife]['children'] |= children\n",
|
||||
" else: individuals[wife] = {'children': children}\n",
|
||||
" except IndexError: pass\n",
|
||||
" try:\n",
|
||||
" husb = g.get_family_members(v, \"HUSB\")[0]\n",
|
||||
" husb = term2id(husb)\n",
|
||||
" if husb in individuals: individuals[husb]['children'] |= children\n",
|
||||
" else: individuals[husb] = {'children': children}\n",
|
||||
" except IndexError: pass\n",
|
||||
"\n",
|
||||
" if wife and husb: marriages[wife + husb] = (term2id(v), wife, husb)\n",
|
||||
"\n",
|
||||
"for idx, val in individuals.items():\n",
|
||||
" added_terms = ''\n",
|
||||
" if val['sex'] == 'f':\n",
|
||||
" parent_predicate, sibl_predicate = \"isMotherOf\", \"isSisterOf\"\n",
|
||||
" else:\n",
|
||||
" parent_predicate, sibl_predicate = \"isFatherOf\", \"isBrotherOf\"\n",
|
||||
" if len(val['children']):\n",
|
||||
" added_terms += \" ;\\n fhkb:\" + parent_predicate + \" \" + \", \".join([\"fhkb:\" + i for i in val['children']])\n",
|
||||
" if len(val['siblings']):\n",
|
||||
" added_terms += \" ;\\n fhkb:\" + sibl_predicate + \" \" + \", \".join([\"fhkb:\" + i for i in val['siblings']])\n",
|
||||
" out.write(\"fhkb:%s a owl:NamedIndividual, owl:Thing%s ;\\n rdfs:label \\\"%s\\\" .\\n\" % (idx, added_terms, val['title']))\n",
|
||||
"\n",
|
||||
"for k, v in marriages.items():\n",
|
||||
" out.write(\"fhkb:%s a owl:NamedIndividual, owl:Thing ;\\n fhkb:hasFemalePartner fhkb:%s ;\\n fhkb:hasMalePartner fhkb:%s .\\n\" % v)\n",
|
||||
"\n",
|
||||
"out.write(\"[] a owl:AllDifferent ;\\n owl:distinctMembers (\")\n",
|
||||
"for idx in individuals.keys():\n",
|
||||
" out.write(\" fhkb:\" + idx)\n",
|
||||
"for k, v in marriages.items():\n",
|
||||
" out.write(\" fhkb:\" + v[0])\n",
|
||||
"out.write(\" ) .\")\n",
|
||||
"out.close()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 8,
|
||||
"metadata": {
|
||||
"trusted": true
|
||||
},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
" fhkb:hasFemalePartner fhkb:i34 ;\n",
|
||||
" fhkb:hasMalePartner fhkb:i33 .\n",
|
||||
"fhkb:i54 a owl:NamedIndividual, owl:Thing ;\n",
|
||||
" fhkb:hasFemalePartner fhkb:i36 ;\n",
|
||||
" fhkb:hasMalePartner fhkb:i35 .\n",
|
||||
"fhkb:i55 a owl:NamedIndividual, owl:Thing ;\n",
|
||||
" fhkb:hasFemalePartner fhkb:i38 ;\n",
|
||||
" fhkb:hasMalePartner fhkb:i37 .\n",
|
||||
"[] a owl:AllDifferent ;\n",
|
||||
" owl:distinctMembers ( fhkb:i0 fhkb:i1 fhkb:i2 fhkb:i3 fhkb:i4 fhkb:i5 fhkb:i6 fhkb:i7 fhkb:i8 fhkb:i9 fhkb:i10 fhkb:i11 fhkb:i12 fhkb:i13 fhkb:i14 fhkb:i15 fhkb:i16 fhkb:i17 fhkb:i18 fhkb:i19 fhkb:i20 fhkb:i21 fhkb:i22 fhkb:i23 fhkb:i24 fhkb:i25 fhkb:i26 fhkb:i27 fhkb:i28 fhkb:i29 fhkb:i30 fhkb:i31 fhkb:i32 fhkb:i33 fhkb:i34 fhkb:i35 fhkb:i36 fhkb:i37 fhkb:i38 fhkb:i39 fhkb:i40 fhkb:i41 fhkb:i42 fhkb:i43 fhkb:i44 fhkb:i45 fhkb:i46 fhkb:i47 fhkb:i48 fhkb:i49 fhkb:i50 fhkb:i51 fhkb:i52 fhkb:i53 fhkb:i54 fhkb:i55 ) ."
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"!tail onto.ttl"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"### 執行推理\n",
|
||||
"\n",
|
||||
"現在我們希望能夠使用這個本體進行推理和查詢。我們將使用 [RDFLib](https://github.com/RDFLib),這是一個用於以不同格式讀取 RDF 圖、查詢等的庫。\n",
|
||||
"\n",
|
||||
"對於邏輯推理,我們將使用 [OWL-RL](https://github.com/RDFLib/OWL-RL) 庫,該庫允許我們構建 RDF 圖的**閉包**,也就是說,添加所有可以推導出的概念和關係。\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 10,
|
||||
"metadata": {
|
||||
"trusted": true
|
||||
},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"Requirement already satisfied: rdflib in /home/rg/anaconda3/envs/ai4beg/lib/python3.11/site-packages (6.3.2)\n",
|
||||
"Requirement already satisfied: isodate<0.7.0,>=0.6.0 in /home/rg/anaconda3/envs/ai4beg/lib/python3.11/site-packages (from rdflib) (0.6.1)\n",
|
||||
"Requirement already satisfied: pyparsing<4,>=2.1.0 in /home/rg/anaconda3/envs/ai4beg/lib/python3.11/site-packages (from rdflib) (3.0.9)\n",
|
||||
"Requirement already satisfied: six in /home/rg/anaconda3/envs/ai4beg/lib/python3.11/site-packages (from isodate<0.7.0,>=0.6.0->rdflib) (1.16.0)\n",
|
||||
"Collecting git+https://github.com/RDFLib/OWL-RL.git\n",
|
||||
" Cloning https://github.com/RDFLib/OWL-RL.git to /tmp/pip-req-build-lbfzwi3m\n",
|
||||
" Running command git clone --filter=blob:none --quiet https://github.com/RDFLib/OWL-RL.git /tmp/pip-req-build-lbfzwi3m\n",
|
||||
" Resolved https://github.com/RDFLib/OWL-RL.git to commit a77e1791b88b54aace609bc6000aac14c7add4ff\n",
|
||||
" Preparing metadata (setup.py) ... \u001b[?25ldone\n",
|
||||
"\u001b[?25hRequirement already satisfied: rdflib>=6.0.2 in /home/rg/anaconda3/envs/ai4beg/lib/python3.11/site-packages (from owlrl==6.0.2) (6.3.2)\n",
|
||||
"Requirement already satisfied: isodate<0.7.0,>=0.6.0 in /home/rg/anaconda3/envs/ai4beg/lib/python3.11/site-packages (from rdflib>=6.0.2->owlrl==6.0.2) (0.6.1)\n",
|
||||
"Requirement already satisfied: pyparsing<4,>=2.1.0 in /home/rg/anaconda3/envs/ai4beg/lib/python3.11/site-packages (from rdflib>=6.0.2->owlrl==6.0.2) (3.0.9)\n",
|
||||
"Requirement already satisfied: six in /home/rg/anaconda3/envs/ai4beg/lib/python3.11/site-packages (from isodate<0.7.0,>=0.6.0->rdflib>=6.0.2->owlrl==6.0.2) (1.16.0)\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"!{sys.executable} -m pip install rdflib\n",
|
||||
"!{sys.executable} -m pip install git+https://github.com/RDFLib/OWL-RL.git"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"讓我們打開本體文件,看看它包含了多少三元組:\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 11,
|
||||
"metadata": {
|
||||
"trusted": true
|
||||
},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"Triplets found:669\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"import rdflib\n",
|
||||
"from owlrl import DeductiveClosure, OWLRL_Extension\n",
|
||||
"\n",
|
||||
"g = rdflib.Graph()\n",
|
||||
"g.parse(\"onto.ttl\", format=\"turtle\")\n",
|
||||
"\n",
|
||||
"print(\"Triplets found:%d\" % len(g))"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"現在讓我們構建閉包,看看三元組的數量如何增加:\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 12,
|
||||
"metadata": {
|
||||
"trusted": true
|
||||
},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"Triplets after inference:4246\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"DeductiveClosure(OWLRL_Extension).expand(g)\n",
|
||||
"print(\"Triplets after inference:%d\" % len(g))"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"### 查詢親屬關係\n",
|
||||
"\n",
|
||||
"現在我們可以查詢圖譜來查看人與人之間的不同關係。我們可以結合使用 **SPARQL** 語言和 `query` 方法。在我們的例子中,讓我們來看看家譜中所有的 **叔叔**:\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 13,
|
||||
"metadata": {
|
||||
"trusted": true
|
||||
},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"Fedor Alekseevich Romanov is uncle of Ekaterina Ivanovna Romanova\n",
|
||||
"Aleksandr I Pavlovich Romanov is uncle of Aleksandr II Nikolaevich Romanov\n",
|
||||
"Fedor Alekseevich Romanov is uncle of Anna Ivanovna Romanova\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"qres = g.query(\n",
|
||||
" \"\"\"SELECT DISTINCT ?aname ?bname\n",
|
||||
" WHERE {\n",
|
||||
" ?a fhkb:isUncleOf ?b .\n",
|
||||
" ?a rdfs:label ?aname .\n",
|
||||
" ?b rdfs:label ?bname .\n",
|
||||
" }\"\"\")\n",
|
||||
"\n",
|
||||
"for row in qres:\n",
|
||||
" print(\"%s is uncle of %s\" % row)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"隨意嘗試不同的家庭關係。例如,您可以查看 `isAncestorOf` 關係,它會遞迴地定義某個人的所有祖先。\n",
|
||||
"\n",
|
||||
"最後,讓我們整理一下!\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 14,
|
||||
"metadata": {
|
||||
"trusted": true
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"!rm onto.ttl"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": []
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"\n---\n\n**免責聲明**: \n本文件使用 AI 翻譯服務 [Co-op Translator](https://github.com/Azure/co-op-translator) 進行翻譯。儘管我們努力確保翻譯的準確性,但請注意,自動翻譯可能包含錯誤或不準確之處。原始文件的母語版本應被視為權威來源。對於關鍵資訊,建議使用專業人工翻譯。我們對因使用此翻譯而引起的任何誤解或錯誤解釋不承擔責任。\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"interpreter": {
|
||||
"hash": "86193a1ab0ba47eac1c69c1756090baa3b420b3eea7d4aafab8b85f8b312f0c5"
|
||||
},
|
||||
"kernelspec": {
|
||||
"display_name": "Python 3.6",
|
||||
"language": "python",
|
||||
"name": "python3"
|
||||
},
|
||||
"language_info": {
|
||||
"codemirror_mode": {
|
||||
"name": "ipython",
|
||||
"version": 3
|
||||
},
|
||||
"file_extension": ".py",
|
||||
"mimetype": "text/x-python",
|
||||
"name": "python",
|
||||
"nbconvert_exporter": "python",
|
||||
"pygments_lexer": "ipython3",
|
||||
"version": "3.11.2"
|
||||
},
|
||||
"coopTranslator": {
|
||||
"original_hash": "6537d5597320e27b6052b4377b8ff8bb",
|
||||
"translation_date": "2025-08-31T10:05:13+00:00",
|
||||
"source_file": "lessons/2-Symbolic/FamilyOntology.ipynb",
|
||||
"language_code": "tw"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 2
|
||||
}
|
||||
|
|
@ -0,0 +1,548 @@
|
|||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"collapsed": true
|
||||
},
|
||||
"source": [
|
||||
"## Microsoft Concept Graph - 此 API 已不再提供,但請查看筆記本以了解概念\n",
|
||||
"\n",
|
||||
"[Microsoft Concept Graph](https://concept.research.microsoft.com/) 是一個從互聯網挖掘的龐大術語分類,包含概念之間的 `is-a` 關係。\n",
|
||||
"\n",
|
||||
"Context Graph 提供以下兩種形式:\n",
|
||||
" * 可下載的大型文本檔案\n",
|
||||
" * REST API\n",
|
||||
"\n",
|
||||
"統計數據:\n",
|
||||
" * 5401933 個獨特概念\n",
|
||||
" * 12551613 個獨特實例\n",
|
||||
" * 87603947 個 `is-a` 關係\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## 使用網路服務\n",
|
||||
"\n",
|
||||
"網路服務提供不同的呼叫方式,用於估算某個概念屬於不同群組的可能性。更多資訊請參考[這裡](https://concept.research.microsoft.com/Home/Api)。 \n",
|
||||
"以下是範例 URL 呼叫:`https://concept.research.microsoft.com/api/Concept/ScoreByProb?instance=microsoft&topK=10`\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 7,
|
||||
"metadata": {
|
||||
"trusted": true
|
||||
},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"{'company': 0.6105356614382954,\n",
|
||||
" 'vendor': 0.08858636677518003,\n",
|
||||
" 'client': 0.048239124001183784,\n",
|
||||
" 'firm': 0.045476965571668145,\n",
|
||||
" 'large company': 0.043109401203511886,\n",
|
||||
" 'organization': 0.043010752688172046,\n",
|
||||
" 'corporation': 0.035908059583703265,\n",
|
||||
" 'brand': 0.03383644076156654,\n",
|
||||
" 'software company': 0.027522935779816515,\n",
|
||||
" 'technology company': 0.023774292196902438}"
|
||||
]
|
||||
},
|
||||
"execution_count": 7,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"import urllib\n",
|
||||
"import json\n",
|
||||
"import ssl\n",
|
||||
"\n",
|
||||
"def http(x):\n",
|
||||
" ssl._create_default_https_context = ssl._create_unverified_context\n",
|
||||
" response = urllib.request.urlopen(x)\n",
|
||||
" data = response.read()\n",
|
||||
" return data.decode('utf-8')\n",
|
||||
"\n",
|
||||
"def query(x):\n",
|
||||
" return json.loads(http(\"https://concept.research.microsoft.com/api/Concept/ScoreByProb?instance={}&topK=10\".format(urllib.parse.quote(x))))\n",
|
||||
"\n",
|
||||
"query('microsoft')"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"讓我們嘗試使用父概念對新聞標題進行分類。要獲取新聞標題,我們將使用 [NewsApi.org](http://newsapi.org) 服務。您需要獲取自己的 API 金鑰才能使用該服務——請訪問網站並註冊免費開發者計劃。\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 20,
|
||||
"metadata": {
|
||||
"trusted": true
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"newsapi_key = '<your API key here>'\n",
|
||||
"def get_news(country='us'):\n",
|
||||
" res = json.loads(http(\"https://newsapi.org/v2/top-headlines?country={0}&apiKey={1}\".format(country,newsapi_key)))\n",
|
||||
" return res['articles']\n",
|
||||
"\n",
|
||||
"all_titles = [x['title'] for x in get_news('us')+get_news('gb')]"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 21,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"['Covid-19 Live Updates: Vaccines and Boosters News - The New York Times',\n",
|
||||
" 'Ukrainians Flee Mariupol as Russian Forces Push to Take Port City - The Wall Street Journal',\n",
|
||||
" 'Bond Yields Jump, Stock Futures Rise After Powell Says Fed Is Ready to Be More Aggressive - The Wall Street Journal',\n",
|
||||
" 'Putin critic Alexei Navalny found guilty by Russian court - New York Post ',\n",
|
||||
" \"Supreme Court nominee Ketanji Brown Jackson will face questions at confirmation hearing's second day - CNN\",\n",
|
||||
" '2 teachers killed at Swedish high school, student arrested - ABC News',\n",
|
||||
" 'Clues to Covid-19’s 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 Women’s 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 Sunak’s 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 Eye’s outtakes show the brutal TV comedy was the tip of an iceberg - The Guardian',\n",
|
||||
" \"Vladimir Putin threatens civilians to break Mariupol's spirit - The Times\",\n",
|
||||
" 'Shell U-turn on Cambo oilfield would threaten green targets, say campaigners - The Guardian',\n",
|
||||
" 'St Helens dog attack: Girl aged 17 months killed at home - BBC',\n",
|
||||
" \"PlayStation to buy 'Assassin's Creed' veteran Jade Raymond's Haven Studios - NME\",\n",
|
||||
" '‘Clear sign’ Putin considering using chemical weapons in Ukraine, claims President Biden - The Independent',\n",
|
||||
" 'NASA confirms there are 5,000 planets outside our solar system - Daily Mail',\n",
|
||||
" 'Nintendo Switch finally has folders • Eurogamer.net - Eurogamer.net',\n",
|
||||
" 'FA to “find a solution” as Liverpool fan group blasts “shambolic” Wembley travel - This Is Anfield',\n",
|
||||
" 'Manchester United transfer news LIVE Erik ten Hag latest and Man Utd manager updates - Manchester Evening News',\n",
|
||||
" 'Inflation raises cost of UK government borrowing in February; crude oil up again – business live - The Guardian',\n",
|
||||
" 'Alexei Navalny: Kremlin critic found guilty of large-scale fraud and contempt of court by Russian court - Sky News',\n",
|
||||
" \"UK prepares to nationalize Russia natural gas giant Gazprom's retail unit - Business Insider\",\n",
|
||||
" 'Zaghari-Ratcliffe: Hunt calls for inquiry into delay over Iran debt payment - The Guardian']"
|
||||
]
|
||||
},
|
||||
"execution_count": 21,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"all_titles"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"首先,我們希望能夠從新聞標題中提取名詞。我們將使用 `TextBlob` 庫來完成這項工作,該庫簡化了許多像這樣的典型自然語言處理任務。\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 15,
|
||||
"metadata": {
|
||||
"trusted": true
|
||||
},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"Requirement already satisfied: textblob in c:\\winapp\\miniconda3\\lib\\site-packages (0.17.1)\n",
|
||||
"Requirement already satisfied: nltk>=3.1 in c:\\winapp\\miniconda3\\lib\\site-packages (from textblob) (3.5)\n",
|
||||
"Requirement already satisfied: joblib in c:\\winapp\\miniconda3\\lib\\site-packages (from nltk>=3.1->textblob) (1.0.1)\n",
|
||||
"Requirement already satisfied: regex in c:\\winapp\\miniconda3\\lib\\site-packages (from nltk>=3.1->textblob) (2021.11.10)\n",
|
||||
"Requirement already satisfied: tqdm in c:\\winapp\\miniconda3\\lib\\site-packages (from nltk>=3.1->textblob) (4.61.2)\n",
|
||||
"Requirement already satisfied: click in c:\\winapp\\miniconda3\\lib\\site-packages (from nltk>=3.1->textblob) (8.0.3)\n",
|
||||
"Requirement already satisfied: colorama in c:\\winapp\\miniconda3\\lib\\site-packages (from click->nltk>=3.1->textblob) (0.4.4)\n",
|
||||
"Finished.\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "stderr",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"[nltk_data] Downloading package brown to\n",
|
||||
"[nltk_data] C:\\Users\\dmitryso\\AppData\\Roaming\\nltk_data...\n",
|
||||
"[nltk_data] Package brown is already up-to-date!\n",
|
||||
"[nltk_data] Downloading package punkt to\n",
|
||||
"[nltk_data] C:\\Users\\dmitryso\\AppData\\Roaming\\nltk_data...\n",
|
||||
"[nltk_data] Package punkt is already up-to-date!\n",
|
||||
"[nltk_data] Downloading package wordnet to\n",
|
||||
"[nltk_data] C:\\Users\\dmitryso\\AppData\\Roaming\\nltk_data...\n",
|
||||
"[nltk_data] Package wordnet is already up-to-date!\n",
|
||||
"[nltk_data] Downloading package averaged_perceptron_tagger to\n",
|
||||
"[nltk_data] C:\\Users\\dmitryso\\AppData\\Roaming\\nltk_data...\n",
|
||||
"[nltk_data] Package averaged_perceptron_tagger is already up-to-\n",
|
||||
"[nltk_data] date!\n",
|
||||
"[nltk_data] Downloading package conll2000 to\n",
|
||||
"[nltk_data] C:\\Users\\dmitryso\\AppData\\Roaming\\nltk_data...\n",
|
||||
"[nltk_data] Package conll2000 is already up-to-date!\n",
|
||||
"[nltk_data] Downloading package movie_reviews to\n",
|
||||
"[nltk_data] C:\\Users\\dmitryso\\AppData\\Roaming\\nltk_data...\n",
|
||||
"[nltk_data] Package movie_reviews is already up-to-date!\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"import sys\n",
|
||||
"!{sys.executable} -m pip install textblob\n",
|
||||
"!{sys.executable} -m textblob.download_corpora\n",
|
||||
"from textblob import TextBlob"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 22,
|
||||
"metadata": {
|
||||
"trusted": true
|
||||
},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"{'covid-19 live updates': 1,\n",
|
||||
" 'vaccines': 1,\n",
|
||||
" 'boosters': 1,\n",
|
||||
" 'york': 4,\n",
|
||||
" 'ukrainians flee mariupol': 1,\n",
|
||||
" 'forces push': 1,\n",
|
||||
" 'port city': 1,\n",
|
||||
" 'wall street journal': 3,\n",
|
||||
" 'bond yields': 1,\n",
|
||||
" 'futures rise': 1,\n",
|
||||
" 'powell says fed': 1,\n",
|
||||
" 'ready': 1,\n",
|
||||
" 'be': 1,\n",
|
||||
" 'aggressive': 1,\n",
|
||||
" 'putin': 3,\n",
|
||||
" 'alexei navalny': 2,\n",
|
||||
" 'russian': 2,\n",
|
||||
" 'supreme court nominee': 1,\n",
|
||||
" 'ketanji brown jackson': 1,\n",
|
||||
" \"confirmation hearing 's\": 1,\n",
|
||||
" 'cnn': 1,\n",
|
||||
" 'swedish': 1,\n",
|
||||
" 'high school': 1,\n",
|
||||
" 'abc': 1,\n",
|
||||
" 'clues': 1,\n",
|
||||
" 'covid-19': 1,\n",
|
||||
" '’ s': 2,\n",
|
||||
" 'moves': 1,\n",
|
||||
" 'sewers': 1,\n",
|
||||
" 'roll dice': 1,\n",
|
||||
" 'jackson': 1,\n",
|
||||
" 'decisions |': 1,\n",
|
||||
" 'thehill': 1,\n",
|
||||
" 'clear': 2,\n",
|
||||
" 'chemical weapons': 2,\n",
|
||||
" 'ukraine': 3,\n",
|
||||
" 'claims president': 2,\n",
|
||||
" 'biden': 2,\n",
|
||||
" 'nasa': 2,\n",
|
||||
" 'solar system': 2,\n",
|
||||
" 'daily mail': 3,\n",
|
||||
" 'us stocks': 1,\n",
|
||||
" 'fed chair powell': 1,\n",
|
||||
" \"'s remarks\": 1,\n",
|
||||
" 'fox': 1,\n",
|
||||
" \"'we 've\": 1,\n",
|
||||
" 'tests': 1,\n",
|
||||
" 'covid': 1,\n",
|
||||
" 'politico': 1,\n",
|
||||
" 'duchess': 1,\n",
|
||||
" 'cambridge': 1,\n",
|
||||
" 'swaps khaki jungle gear': 1,\n",
|
||||
" 'vampire': 1,\n",
|
||||
" 'wife': 1,\n",
|
||||
" 'belize': 1,\n",
|
||||
" 'china': 2,\n",
|
||||
" 'flight recorders': 1,\n",
|
||||
" 'plane crash': 1,\n",
|
||||
" 'reuters': 2,\n",
|
||||
" 'russian oligarch': 1,\n",
|
||||
" 'abramovich': 1,\n",
|
||||
" 'live': 1,\n",
|
||||
" 'russia': 2,\n",
|
||||
" 'stops talks': 1,\n",
|
||||
" 'japan': 1,\n",
|
||||
" 'español': 1,\n",
|
||||
" 'powers remain': 1,\n",
|
||||
" 'threats lurk': 1,\n",
|
||||
" 'set': 1,\n",
|
||||
" 'webb': 1,\n",
|
||||
" 'telescope begins multi-instrument alignment': 1,\n",
|
||||
" 'scitechdaily': 1,\n",
|
||||
" 'uconn': 1,\n",
|
||||
" 'ucf': 1,\n",
|
||||
" 'ncaa': 1,\n",
|
||||
" \"women 's tournament second-round highlights\": 1,\n",
|
||||
" 'march madness': 1,\n",
|
||||
" 'bucking republican trend': 1,\n",
|
||||
" 'indiana': 1,\n",
|
||||
" 'vetoes transgender': 1,\n",
|
||||
" 'bill': 1,\n",
|
||||
" 'maggie fox': 1,\n",
|
||||
" 'coronation': 1,\n",
|
||||
" 'shameless': 1,\n",
|
||||
" \"'sudden accident\": 1,\n",
|
||||
" 'mirror online': 1,\n",
|
||||
" 'mirror': 2,\n",
|
||||
" 'plane crash –': 1,\n",
|
||||
" 'search': 1,\n",
|
||||
" 'moment flight': 1,\n",
|
||||
" 'daniel morgan': 1,\n",
|
||||
" 'report condemns': 1,\n",
|
||||
" 'met': 1,\n",
|
||||
" 'guardian': 6,\n",
|
||||
" 'rishi sunak': 1,\n",
|
||||
" '’ s spring': 1,\n",
|
||||
" 'statement': 1,\n",
|
||||
" 'bbc.com': 1,\n",
|
||||
" 'uk': 3,\n",
|
||||
" 'ireland': 1,\n",
|
||||
" 'euro': 1,\n",
|
||||
" 'vladimir putin': 2,\n",
|
||||
" \"'s 'lover\": 1,\n",
|
||||
" 'brass eye': 1,\n",
|
||||
" '’ s outtakes': 1,\n",
|
||||
" 'brutal tv comedy': 1,\n",
|
||||
" 'threatens civilians': 1,\n",
|
||||
" 'mariupol': 1,\n",
|
||||
" \"'s spirit\": 1,\n",
|
||||
" 'shell u-turn': 1,\n",
|
||||
" 'cambo': 1,\n",
|
||||
" 'green targets': 1,\n",
|
||||
" 'st helens': 1,\n",
|
||||
" 'dog attack': 1,\n",
|
||||
" 'girl': 1,\n",
|
||||
" 'bbc': 1,\n",
|
||||
" 'playstation': 1,\n",
|
||||
" \"'assassin 's\": 1,\n",
|
||||
" 'creed': 1,\n",
|
||||
" 'jade raymond': 1,\n",
|
||||
" 'haven studios': 1,\n",
|
||||
" 'nme': 1,\n",
|
||||
" 'nintendo switch': 1,\n",
|
||||
" 'folders •': 1,\n",
|
||||
" 'eurogamer.net': 2,\n",
|
||||
" 'fa': 1,\n",
|
||||
" 'solution ”': 1,\n",
|
||||
" 'liverpool': 1,\n",
|
||||
" 'fan group blasts “ shambolic ”': 1,\n",
|
||||
" 'wembley': 1,\n",
|
||||
" 'anfield': 1,\n",
|
||||
" 'manchester': 1,\n",
|
||||
" 'live erik': 1,\n",
|
||||
" 'hag': 1,\n",
|
||||
" 'utd': 1,\n",
|
||||
" 'manager updates': 1,\n",
|
||||
" 'manchester evening': 1,\n",
|
||||
" 'inflation': 1,\n",
|
||||
" 'government borrowing': 1,\n",
|
||||
" 'february': 1,\n",
|
||||
" 'crude oil': 1,\n",
|
||||
" '– business': 1,\n",
|
||||
" 'kremlin': 1,\n",
|
||||
" 'large-scale fraud': 1,\n",
|
||||
" 'sky': 1,\n",
|
||||
" 'natural gas': 1,\n",
|
||||
" 'gazprom': 1,\n",
|
||||
" 'retail unit': 1,\n",
|
||||
" 'insider': 1,\n",
|
||||
" 'zaghari-ratcliffe': 1,\n",
|
||||
" 'hunt': 1,\n",
|
||||
" 'iran': 1,\n",
|
||||
" 'debt payment': 1}"
|
||||
]
|
||||
},
|
||||
"execution_count": 22,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"w = {}\n",
|
||||
"for x in all_titles:\n",
|
||||
" for n in TextBlob(x).noun_phrases:\n",
|
||||
" if n in w:\n",
|
||||
" w[n].append(x)\n",
|
||||
" else:\n",
|
||||
" w[n]=[x]\n",
|
||||
"{ x:len(w[x]) for x in w.keys()}"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"我們可以看到名詞並未給我們大的主題群組。讓我們用從概念圖中獲得的更一般的術語來替代名詞。這將需要一些時間,因為我們正在為每個名詞短語進行 REST 調用。\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 23,
|
||||
"metadata": {
|
||||
"trusted": true
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"w = {}\n",
|
||||
"for x in all_titles:\n",
|
||||
" for noun in TextBlob(x).noun_phrases:\n",
|
||||
" terms = query(noun.replace(' ','%20'))\n",
|
||||
" for term in [u for u in terms.keys() if terms[u]>0.1]:\n",
|
||||
" if term in w:\n",
|
||||
" w[term].append(x)\n",
|
||||
" else:\n",
|
||||
" w[term]=[x]"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 24,
|
||||
"metadata": {
|
||||
"trusted": true
|
||||
},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"{'city': 9,\n",
|
||||
" 'brand': 4,\n",
|
||||
" 'place': 9,\n",
|
||||
" 'town': 4,\n",
|
||||
" 'factor': 4,\n",
|
||||
" 'film': 4,\n",
|
||||
" 'nation': 11,\n",
|
||||
" 'state': 5,\n",
|
||||
" 'person': 4,\n",
|
||||
" 'organization': 5,\n",
|
||||
" 'publication': 10,\n",
|
||||
" 'market': 5,\n",
|
||||
" 'economy': 4,\n",
|
||||
" 'company': 6,\n",
|
||||
" 'newspaper': 6,\n",
|
||||
" 'relationship': 6}"
|
||||
]
|
||||
},
|
||||
"execution_count": 24,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"{ x:len(w[x]) for x in w.keys() if len(w[x])>3}"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 27,
|
||||
"metadata": {
|
||||
"trusted": true
|
||||
},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"\n",
|
||||
"ECONOMY:\n",
|
||||
"China searches for victims, flight recorders after first plane crash in 12 years - Reuters\n",
|
||||
"Live updates: Russia stops talks with Japan over sanctions - The Associated Press - en Español\n",
|
||||
"China plane crash – live: Search for survivors continues as witness describes moment flight fell from sky - The Independent\n",
|
||||
"UK prepares to nationalize Russia natural gas giant Gazprom's retail unit - Business Insider\n",
|
||||
"\n",
|
||||
"NATION:\n",
|
||||
"‘Clear sign’ Putin considering using chemical weapons in Ukraine, claims President Biden - The Independent\n",
|
||||
"Duchess of Cambridge swaps khaki jungle gear for Vampire's Wife dress on Belize trip - Daily Mail\n",
|
||||
"China searches for victims, flight recorders after first plane crash in 12 years - Reuters\n",
|
||||
"Live updates: Russia stops talks with Japan over sanctions - The Associated Press - en Español\n",
|
||||
"Live updates: Russia stops talks with Japan over sanctions - The Associated Press - en Español\n",
|
||||
"China plane crash – live: Search for survivors continues as witness describes moment flight fell from sky - The Independent\n",
|
||||
"UK and Republic of Ireland in line to host Euro 2028 after no one else bids - The Guardian\n",
|
||||
"Friends beg Vladimir Putin's 'lover' to persuade him to end Ukraine invasion - The Mirror\n",
|
||||
"‘Clear sign’ Putin considering using chemical weapons in Ukraine, claims President Biden - The Independent\n",
|
||||
"UK prepares to nationalize Russia natural gas giant Gazprom's retail unit - Business Insider\n",
|
||||
"Zaghari-Ratcliffe: Hunt calls for inquiry into delay over Iran debt payment - The Guardian\n",
|
||||
"\n",
|
||||
"PERSON:\n",
|
||||
"‘Clear sign’ Putin considering using chemical weapons in Ukraine, claims President Biden - The Independent\n",
|
||||
"Duchess of Cambridge swaps khaki jungle gear for Vampire's Wife dress on Belize trip - Daily Mail\n",
|
||||
"Second superyacht linked to Russian oligarch Abramovich docks in Turkey - Reuters\n",
|
||||
"‘Clear sign’ Putin considering using chemical weapons in Ukraine, claims President Biden - The Independent\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"print('\\nECONOMY:\\n'+'\\n'.join(w['economy']))\n",
|
||||
"print('\\nNATION:\\n'+'\\n'.join(w['nation']))\n",
|
||||
"print('\\nPERSON:\\n'+'\\n'.join(w['person']))"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"\n---\n\n**免責聲明**: \n本文件已使用 AI 翻譯服務 [Co-op Translator](https://github.com/Azure/co-op-translator) 進行翻譯。雖然我們致力於提供準確的翻譯,但請注意,自動翻譯可能包含錯誤或不準確之處。原始文件的母語版本應被視為權威來源。對於關鍵資訊,建議使用專業人工翻譯。我們對因使用此翻譯而引起的任何誤解或錯誤解釋不承擔責任。\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"kernelspec": {
|
||||
"display_name": "Python 3.7.4 64-bit (conda)",
|
||||
"metadata": {
|
||||
"interpreter": {
|
||||
"hash": "86193a1ab0ba47eac1c69c1756090baa3b420b3eea7d4aafab8b85f8b312f0c5"
|
||||
}
|
||||
},
|
||||
"name": "python3"
|
||||
},
|
||||
"language_info": {
|
||||
"codemirror_mode": {
|
||||
"name": "ipython",
|
||||
"version": 3
|
||||
},
|
||||
"file_extension": ".py",
|
||||
"mimetype": "text/x-python",
|
||||
"name": "python",
|
||||
"nbconvert_exporter": "python",
|
||||
"pygments_lexer": "ipython3",
|
||||
"version": "3.9.5"
|
||||
},
|
||||
"coopTranslator": {
|
||||
"original_hash": "7b3f1fc049371bcf8649ac9fefdf0413",
|
||||
"translation_date": "2025-10-03T18:51:56+00:00",
|
||||
"source_file": "lessons/2-Symbolic/MSConceptGraph.ipynb",
|
||||
"language_code": "tw"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 2
|
||||
}
|
||||
|
|
@ -0,0 +1,248 @@
|
|||
# 知識表示與專家系統
|
||||
|
||||

|
||||
|
||||
> 筆記作者:[Tomomi Imura](https://twitter.com/girlie_mac)
|
||||
|
||||
人工智慧的追求是基於對知識的探索,以類似人類理解世界的方式來解釋世界。但你該如何達成這個目標呢?
|
||||
|
||||
## [課前測驗](https://ff-quizzes.netlify.app/en/ai/quiz/3)
|
||||
|
||||
在 AI 的早期階段,自頂向下創建智能系統的方法(在前一課討論過)非常受歡迎。這個想法是將人類的知識提取成機器可讀的形式,然後用它來自動解決問題。這種方法基於兩大核心理念:
|
||||
|
||||
* 知識表示
|
||||
* 推理
|
||||
|
||||
## 知識表示
|
||||
|
||||
符號 AI 中一個重要的概念是**知識**。區分知識和*資訊*或*數據*非常重要。例如,可以說書籍包含知識,因為我們通過學習書籍可以成為專家。然而,書中實際包含的是所謂的*數據*,透過閱讀書籍並將這些數據整合到我們的世界模型中,我們將數據轉變為知識。
|
||||
|
||||
> ✅ **知識** 是指存在於我們腦海中,代表我們對世界理解的東西。它是透過一個主動的**學習**過程獲得,將我們收到的資訊片段整合進我們的活躍世界模型中。
|
||||
|
||||
通常,我們不會嚴格定義知識,而是透過[DIKW 金字塔](https://en.wikipedia.org/wiki/DIKW_pyramid)將其與其他相關概念對齊。包含以下概念:
|
||||
|
||||
* **數據(Data)** 是指表現在物理媒介上的東西,如書面文字或口語。數據獨立於人類存在,且可在不同人間傳遞。
|
||||
* **資訊(Information)** 是我們對數據的解讀。例如,當我們聽到「電腦」這個字時,就會對它有所理解。
|
||||
* **知識(Knowledge)** 是將資訊整合到我們的世界模型中。例如,一旦學習到電腦是什麼,我們就開始瞭解它如何運作、價格多少、用途為何。這種相互關聯的概念網絡形成了我們的知識。
|
||||
* **智慧(Wisdom)** 是我們對世界理解的更高層次,代表*元知識*,例如有關知識應用的時機與方式的概念。
|
||||
|
||||
<img src="../../../../translated_images/zh-TW/DIKW_Pyramid.94126f7d2bd8db5b.webp" width="30%"/>
|
||||
|
||||
*圖片來源 [維基百科](https://commons.wikimedia.org/w/index.php?curid=37705247),作者 Longlivetheux 自作,CC BY-SA 4.0*
|
||||
|
||||
因此,**知識表示** 問題是尋找一些有效的方法來以資料形式在電腦內代表知識,使其可被自動使用。這可視為一個光譜:
|
||||
|
||||

|
||||
|
||||
> 圖片來源 [Dmitry Soshnikov](http://soshnikov.com)
|
||||
|
||||
* 左邊是電腦可有效使用的非常簡單的知識表示類型。最簡單的是算法式表示,當知識以電腦程式形式存在。然而,這並非最佳的知識表示方式,因為它不具彈性。腦中知識通常是非算法的。
|
||||
* 右邊則是自然文字等表示,它功能最強大,但無法用於自動推理。
|
||||
|
||||
> ✅ 花一分鐘想想你如何在腦海中表達知識,並將它轉換成筆記。是否有特定格式有助於記憶保留?
|
||||
|
||||
## 電腦知識表示的分類
|
||||
|
||||
我們可以將不同的電腦知識表示方法分為以下類別:
|
||||
|
||||
* **網絡表示** 基於我們腦中存在互相關聯的概念網絡。可嘗試在電腦中以圖形方式重現這些網絡,也就是所謂的**語意網絡**。
|
||||
|
||||
1. **物件-屬性-值三元組** 或 **屬性-值對**。由於電腦中圖形可用節點與邊的列表來表示,於是可用物件、屬性和值組成的三元組來表現語意網絡。例如關於程式語言,可建立以下三元組:
|
||||
|
||||
物件 | 屬性 | 值
|
||||
-------|-----------|------
|
||||
Python | 是 | 無型別程式語言
|
||||
Python | 發明者 | Guido van Rossum
|
||||
Python | 區塊語法 | 縮排
|
||||
無型別程式語言 | 不具備 | 型別定義
|
||||
|
||||
> ✅ 想想三元組如何用來表示其他類型的知識。
|
||||
|
||||
2. **階層表示** 強調我們常在腦中建立物件階層。例如,我們知道金絲雀是鳥類,鳥類都有翅膀,且了解金絲雀通常的顏色和飛行速度。
|
||||
|
||||
- **框架表示(Frame representation)** 以每個物件或物件類別表示為一個**框架**,內含**插槽(slots)**。插槽可能有預設值、值限制,或可呼叫的程序來獲取插槽值。所有框架形成類似物件導向程式語言中的物件階層。
|
||||
- **場景(Scenarios)** 是一種特殊的框架,用來表示可能隨時間展開的複雜情境。
|
||||
|
||||
**Python**
|
||||
|
||||
插槽 | 值 | 預設值 | 範圍 |
|
||||
-----|-------|---------------|----------|
|
||||
名稱 | Python | | |
|
||||
是屬於 | 無型別程式語言 | | |
|
||||
變數命名格式 | | CamelCase | |
|
||||
程式長度 | | | 5-5000 行 |
|
||||
區塊語法 | 縮排 | | |
|
||||
|
||||
3. **程序式表示** 基於以一組動作表示知識,於特定條件下執行這些動作。
|
||||
- 生產規則是 if-then 陳述句,可用來推理結論。例如,醫生可能有規則:**如果** 病人體溫高 **或** 血液檢查中的 C 反應蛋白指數高 **那麼** 他有發炎。一旦遇到條件,即可推論發炎,並用於後續推理。
|
||||
- 演算法也可視為另一種程序式表示,儘管在基於知識的系統中幾乎不直接使用。
|
||||
|
||||
4. **邏輯** 起始於亞里斯多德提出作為表達普遍人類知識的方法。
|
||||
- 謂詞邏輯作為數學理論過於豐富,無法完全計算,因此通常使用某些子集,如 Prolog 中的 Horn 子句。
|
||||
- 描述邏輯是一系列用於表示與推論階層物件及分散式知識表示(如*語意網*)的邏輯系統。
|
||||
|
||||
## 專家系統
|
||||
|
||||
符號 AI 的早期成功之一是所謂的**專家系統** — 設計成在有限問題領域中,充當專家的電腦系統。它們基於由一人或多位專家提取的**知識庫**,並含有執行推理的**推理引擎**。
|
||||
|
||||
 | 
|
||||
---------------------------------------------|------------------------------------------------
|
||||
人類神經系統簡化結構 | 知識基礎系統架構
|
||||
|
||||
專家系統的構造類似人類推理系統,包含**短期記憶**和**長期記憶**。類似地,知識基礎系統區分以下元件:
|
||||
|
||||
* **問題記憶**:包含當前正解決問題的知識,如患者的體溫或血壓,是否有發炎等。此知識稱為**靜態知識**,因為它記錄目前問題的快照,也稱為*問題狀態*。
|
||||
* **知識庫**:代表問題領域的長期知識。手動從人類專家提取,不會因諮詢而變動。它讓我們能從一個問題狀態轉到另一個,也稱為**動態知識**。
|
||||
* **推理引擎**:協調整個問題狀態空間中的搜尋過程,必要時向使用者提問,也負責找出適用於每個問題狀態的規則。
|
||||
|
||||
以下為根據動物物理特徵判斷動物的專家系統示例:
|
||||
|
||||

|
||||
|
||||
> 圖片來源 [Dmitry Soshnikov](http://soshnikov.com)
|
||||
|
||||
此圖稱為**AND-OR 樹**,是生產規則集的圖形表示。繪製樹狀圖有助於初期從專家處提取知識。在電腦中表示知識則較方便用規則表示:
|
||||
|
||||
```
|
||||
IF the animal eats meat
|
||||
OR (animal has sharp teeth
|
||||
AND animal has claws
|
||||
AND animal has forward-looking eyes
|
||||
)
|
||||
THEN the animal is a carnivore
|
||||
```
|
||||
|
||||
你可以發現,規則左側條件與動作本質上是物件-屬性-值 (OAV) 三元組。**工作記憶** 包含當前正解決問題的 OAV 三元組集合。**規則引擎** 尋找符合條件的規則並執行,向工作記憶新增三元組。
|
||||
|
||||
> ✅ 嘗試寫一個你喜歡主題的 AND-OR 樹!
|
||||
|
||||
### 前向推理 vs. 後向推理
|
||||
|
||||
上述過程稱為**前向推理**。它從工作記憶中已知的初始資料開始,然後執行以下推理循環:
|
||||
|
||||
1. 若目標屬性已存在於工作記憶中,停止並返回結果。
|
||||
2. 找出所有條件當前成立的規則,得到**衝突集**。
|
||||
3. 執行**衝突解決** — 選擇本步執行的規則。衝突解決策略包括:
|
||||
- 選擇知識庫中第一個適用的規則
|
||||
- 隨機選擇一條規則
|
||||
- 選擇*更具體*的規則,即符合條件(左側)最多的規則
|
||||
4. 執行選中規則,將新知識插入問題狀態中。
|
||||
5. 從步驟 1 重複。
|
||||
|
||||
但有些情況下,我們希望從空白知識開始,透過提問來導致結論。例如醫療診斷通常不會事先做完所有檢驗,而是根據診斷需要才決定檢驗項目。
|
||||
|
||||
該過程可用**後向推理** 模型。它是由**目標**驅動 — 我們想找到的屬性值:
|
||||
|
||||
1. 選擇所有能得出目標值的規則 (目標在右側) — 衝突集
|
||||
2. 若找不到相關規則,或有規則指示需直接詢問使用者,則詢問;否則:
|
||||
3. 透過衝突解決策略選一條作為*假設*,嘗試證實它
|
||||
4. 遞迴處理規則左側所有屬性,嘗試證明它們為目標
|
||||
5. 若任何時刻失敗,返回步驟 3 換用其他規則。
|
||||
|
||||
> ✅ 何種情況適合使用前向推理?又何時適合後向推理?
|
||||
|
||||
### 專家系統的實作
|
||||
|
||||
專家系統可以用不同工具實作:
|
||||
|
||||
* 使用高階程式語言直接編寫。這並非最佳選擇,因為知識基礎系統的主要優勢是知識與推理分離,並且理想狀況下,問題領域專家應能不必瞭解推理細節而撰寫規則。
|
||||
* 使用**專家系統外殼**,即專為輸入知識而設計,並以某種知識表示語言編寫的系統。
|
||||
|
||||
## ✍️ 練習:動物推理
|
||||
|
||||
請參見 [Animals.ipynb](https://github.com/microsoft/AI-For-Beginners/blob/main/lessons/2-Symbolic/Animals.ipynb) 了解前向與後向推理專家系統的範例實作。
|
||||
|
||||
> **註**:此範例相當簡單,只是展示專家系統的概念。當你開始建立這類系統,至少達到約 200 條規則以上時,系統才會展現某種*智能*行為。某個階段,規則變得過於複雜而難以完全記憶,屆時你可能會想知道系統為什麼做出特定決策。不過,知識基礎系統重要特點是可以隨時*解釋*決策的產生過程。
|
||||
|
||||
## 本體論與語意網
|
||||
|
||||
20 世紀末,出現一個倡議,使用知識表示為網際網路資源做標註,以便能找到非常具體查詢對應的資源。這個運動稱為**語意網**,涵蓋以下概念:
|
||||
|
||||
- 一種基於**[描述邏輯](https://en.wikipedia.org/wiki/Description_logic)**(DL)的特殊知識表示。它類似框架知識表示,建構具有屬性的物件階層,但具備形式邏輯語義及推理功能。描述邏輯有整個系列,用以權衡表達力與推理演算法的複雜度。
|
||||
- 分散式知識表示,所有概念都用全球唯一的 URI 識別符號表示,使得可以建立跨網際網路的知識階層。
|
||||
- 一族基於 XML 的知識描述語言:RDF(資源描述框架)、RDFS(RDF 架構)、OWL(本體網路語言)。
|
||||
|
||||
語義網的一個核心概念是**本體(Ontology)**。指的是使用某種形式化知識表示對問題領域進行明確規範。最簡單的本體只是一個問題領域中的物件階層,但更複雜的本體會包含可用於推理的規則。
|
||||
|
||||
在語義網中,所有表示均基於三元組。每個物件和每個關係均由 URI 唯一標識。例如,如果我們想要陳述這個 AI 課程是由 Dmitry Soshnikov 於 2022 年 1 月 1 日開發的事實——我們可以使用以下三元組:
|
||||
|
||||
<img src="../../../../translated_images/zh-TW/triplet.4b9b332587593298.webp" width="30%"/>
|
||||
|
||||
```
|
||||
http://github.com/microsoft/ai-for-beginners http://www.example.com/terms/creation-date “Jan 1, 2022”
|
||||
http://github.com/microsoft/ai-for-beginners http://purl.org/dc/elements/1.1/creator http://soshnikov.com
|
||||
```
|
||||
|
||||
> ✅ 此處的 `http://www.example.com/terms/creation-date` 和 `http://purl.org/dc/elements/1.1/creator` 是表達*創建者*和*創建日期*概念的一些知名且被普遍接受的 URI。
|
||||
|
||||
在較複雜的情況下,如果我們想定義一個創建者列表,可以使用 RDF 中定義的一些資料結構。
|
||||
|
||||
<img src="../../../../translated_images/zh-TW/triplet-complex.32094972c7b4441b.webp" width="40%"/>
|
||||
|
||||
> 上述圖由 [Dmitry Soshnikov](http://soshnikov.com) 製作
|
||||
|
||||
語義網的建設進展在某種程度上被搜尋引擎和自然語言處理技術的成功所放慢,這些技術允許從文本中抽取結構化資料。然而,在某些領域中,仍有大量努力用於維護本體和知識庫。值得注意的幾個專案:
|
||||
|
||||
* [WikiData](https://wikidata.org/) 是一個機器可讀的知識庫集合,與維基百科相關聯。大多數資料來源於維基百科的 *InfoBoxes*,即維基百科頁面內的結構化內容片段。你可以用 SPARQL(一種語義網專用查詢語言)[查詢](https://query.wikidata.org/) wikidata。這裡是一個顯示人類最常見眼睛顏色的範例查詢:
|
||||
|
||||
```sparql
|
||||
#defaultView:BubbleChart
|
||||
SELECT ?eyeColorLabel (COUNT(?human) AS ?count)
|
||||
WHERE
|
||||
{
|
||||
?human wdt:P31 wd:Q5. # human instance-of homo sapiens
|
||||
?human wdt:P1340 ?eyeColor. # human eye-color ?eyeColor
|
||||
SERVICE wikibase:label { bd:serviceParam wikibase:language "en". }
|
||||
}
|
||||
GROUP BY ?eyeColorLabel
|
||||
```
|
||||
|
||||
* [DBpedia](https://www.dbpedia.org/) 是另一個類似 WikiData 的努力。
|
||||
|
||||
> ✅ 如果你想嘗試建立自己的本體,或者開啟現有的本體,可以使用一個很棒的視覺化本體編輯器 [Protégé](https://protege.stanford.edu/)。下載它或線上使用。
|
||||
|
||||
<img src="../../../../translated_images/zh-TW/protege.274177ceeac13b38.webp" width="70%"/>
|
||||
|
||||
*Web Protégé 編輯器開啟羅曼諾夫家族本體。截圖由 Dmitry Soshnikov 提供*
|
||||
|
||||
## ✍️ 練習:家族本體
|
||||
|
||||
|
||||
請參閱 [FamilyOntology.ipynb](https://github.com/Ezana135/AI-For-Beginners/blob/main/lessons/2-Symbolic/FamilyOntology.ipynb) 範例,演示如何使用語義網技術推理家庭關係。我們將採用以常見 GEDCOM 格式表示的家譜和一個家庭關係本體,為給定成員集建立所有家庭關係的圖譜。
|
||||
|
||||
## Microsoft 概念圖譜
|
||||
|
||||
在大多數情況下,本體是透過人工仔細建立的。然而,也可以從非結構化資料中**挖掘**本體,例如從自然語言文本中。
|
||||
|
||||
其中一次嘗試由微軟研究院進行,結果是 [Microsoft Concept Graph](https://blogs.microsoft.com/ai/microsoft-researchers-release-graph-that-helps-machines-conceptualize/?WT.mc_id=academic-77998-cacaste)。
|
||||
|
||||
它是使用 `is-a` 繼承關係聚合在一起的大量實體集合。允許回答「微軟是什麼?」這樣的問題——答案可能是「87% 機率是一家公司,75% 機率是一個品牌」。
|
||||
|
||||
該圖譜可透過 REST API 使用,或作為一個大型可下載的文本檔列出所有實體對。
|
||||
|
||||
## ✍️ 練習:概念圖譜
|
||||
|
||||
試試 [MSConceptGraph.ipynb](https://github.com/microsoft/AI-For-Beginners/blob/main/lessons/2-Symbolic/MSConceptGraph.ipynb) 筆記本,看看如何使用 Microsoft 概念圖譜將新聞文章分組為幾個類別。
|
||||
|
||||
## 結論
|
||||
|
||||
當前,AI 常被視為*機器學習*或*神經網路*的代名詞。然而,人類也展示出顯式推理,這是當前神經網絡尚未處理的東西。在實際專案中,顯式推理仍用於執行需要解釋或能以受控方式調整系統行為的任務。
|
||||
|
||||
## 🚀 挑戰
|
||||
|
||||
在本課程相關的家族本體筆記本中,提供了嘗試其他家庭關係的機會。試著發現家譜中人物之間的新連結。
|
||||
|
||||
## [課後小測驗](https://ff-quizzes.netlify.app/en/ai/quiz/4)
|
||||
|
||||
## 複習與自學
|
||||
|
||||
在網路上搜尋人類試圖量化與編碼知識的領域。查看布魯姆分類法,並回顧歷史,瞭解人類如何嘗試理解世界。探索林奈製作生物分類法的工作,並觀察德米特里·孟德爾葉夫如何創造化學元素的描述與分組方式。還能找到什麼其他有趣的例子?
|
||||
|
||||
**作業**: [建立本體](assignment.md)
|
||||
|
||||
---
|
||||
|
||||
<!-- CO-OP TRANSLATOR DISCLAIMER START -->
|
||||
**免責聲明**:
|
||||
本文件係使用 AI 翻譯服務 [Co-op Translator](https://github.com/Azure/co-op-translator) 翻譯而成。雖然我們致力於確保準確性,但請注意,自動翻譯可能包含錯誤或不準確之處。原始文件之母語版本應視為權威來源。對於重要資訊,建議尋求專業人工翻譯。我們不對因使用本翻譯而產生之任何誤解或誤釋負責。
|
||||
<!-- CO-OP TRANSLATOR DISCLAIMER END -->
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
# 建立本體
|
||||
|
||||
建立知識庫的核心在於對某個主題進行事實模型的分類。選擇一個主題,例如人物、地點或事物,然後建立該主題的模型。使用本課中描述的一些技術和模型構建策略。一個例子可能是建立一個客廳的本體,包括家具、燈光等等。客廳與廚房有什麼不同?浴室呢?你如何判斷它是客廳而不是餐廳?使用 [Protégé](https://protege.stanford.edu/) 來建立你的本體。
|
||||
|
||||
**免責聲明**:
|
||||
本文件使用 AI 翻譯服務 [Co-op Translator](https://github.com/Azure/co-op-translator) 進行翻譯。我們致力於提供準確的翻譯,但請注意,自動翻譯可能包含錯誤或不準確之處。應以原始語言的文件作為權威來源。對於關鍵資訊,建議尋求專業人工翻譯。我們對因使用此翻譯而引起的任何誤解或誤讀概不負責。
|
||||
File diff suppressed because one or more lines are too long
|
|
@ -0,0 +1,95 @@
|
|||
# 神經網路入門:感知器
|
||||
|
||||
## [課前測驗](https://ff-quizzes.netlify.app/en/ai/quiz/5)
|
||||
|
||||
1957年,康奈爾航空實驗室的 Frank Rosenblatt 嘗試實現了一種類似現代神經網路的模型。這是一個名為 "Mark-1" 的硬體實現,設計用來識別基本幾何圖形,例如三角形、正方形和圓形。
|
||||
|
||||
| | |
|
||||
|--------------|-----------|
|
||||
|<img src='../../../../../translated_images/zh-TW/Rosenblatt-wikipedia.294821b285ac796d.webp' alt='Frank Rosenblatt'/> | <img src='../../../../../translated_images/zh-TW/Mark_I_perceptron_wikipedia.1f84eaa2d4b76ec9.webp' alt='The Mark 1 Perceptron' />|
|
||||
|
||||
> 圖片來源:[維基百科](https://en.wikipedia.org/wiki/Perceptron)
|
||||
|
||||
輸入圖像由 20x20 的光電陣列表示,因此神經網路有 400 個輸入和一個二元輸出。一個簡單的網路包含一個神經元,也被稱為 **閾值邏輯單元**。神經網路的權重類似於電位器,需要在訓練階段手動調整。
|
||||
|
||||
> ✅ 電位器是一種允許使用者調整電路阻抗的裝置。
|
||||
|
||||
> 《紐約時報》當時對感知器的描述是:*一種電子計算機的胚胎,[海軍]期望它能夠行走、說話、看見、書寫、自我複製並意識到自己的存在。*
|
||||
|
||||
## 感知器模型
|
||||
|
||||
假設我們的模型有 N 個特徵,此時輸入向量將是一個大小為 N 的向量。感知器是一種 **二元分類** 模型,也就是說它可以區分兩類輸入數據。我們假設對於每個輸入向量 x,感知器的輸出將是 +1 或 -1,取決於所屬的類別。輸出通過以下公式計算:
|
||||
|
||||
y(x) = f(w<sup>T</sup>x)
|
||||
|
||||
其中 f 是一個階梯激活函數
|
||||
|
||||
<!-- img src="http://www.sciweavers.org/tex2img.php?eq=f%28x%29%20%3D%20%5Cbegin%7Bcases%7D%0A%20%20%20%20%20%20%20%20%20%2B1%20%26%20x%20%5Cgeq%200%20%5C%5C%0A%20%20%20%20%20%20%20%20%20-1%20%26%20x%20%3C%200%0A%20%20%20%20%20%20%20%5Cend%7Bcases%7D%20%5C%5C%0A&bc=White&fc=Black&im=jpg&fs=12&ff=arev&edit=0" align="center" border="0" alt="f(x) = \begin{cases} +1 & x \geq 0 \\ -1 & x < 0 \end{cases} \\" width="154" height="50" / -->
|
||||
<img src="../../../../../translated_images/zh-TW/activation-func.b4924007c7ce7764.webp"/>
|
||||
|
||||
## 訓練感知器
|
||||
|
||||
要訓練感知器,我們需要找到一個權重向量 w,使得大部分數據能夠被正確分類,也就是使 **誤差** 最小化。這個誤差 E 根據 **感知器準則** 定義如下:
|
||||
|
||||
E(w) = -∑w<sup>T</sup>x<sub>i</sub>t<sub>i</sub>
|
||||
|
||||
其中:
|
||||
|
||||
* 求和僅針對那些分類錯誤的訓練數據點 i
|
||||
* x<sub>i</sub> 是輸入數據,t<sub>i</sub> 對應於負例和正例分別為 -1 或 +1。
|
||||
|
||||
這個準則被視為權重 w 的函數,我們需要對其進行最小化。通常使用一種稱為 **梯度下降** 的方法,我們從某個初始權重 w<sup>(0)</sup> 開始,然後在每一步根據以下公式更新權重:
|
||||
|
||||
w<sup>(t+1)</sup> = w<sup>(t)</sup> - η∇E(w)
|
||||
|
||||
其中 η 是所謂的 **學習率**,∇E(w) 表示 E 的 **梯度**。計算梯度後,我們得到:
|
||||
|
||||
w<sup>(t+1)</sup> = w<sup>(t)</sup> + ∑ηx<sub>i</sub>t<sub>i</sub>
|
||||
|
||||
Python 中的算法如下:
|
||||
|
||||
```python
|
||||
def train(positive_examples, negative_examples, num_iterations = 100, eta = 1):
|
||||
|
||||
weights = [0,0,0] # Initialize weights (almost randomly :)
|
||||
|
||||
for i in range(num_iterations):
|
||||
pos = random.choice(positive_examples)
|
||||
neg = random.choice(negative_examples)
|
||||
|
||||
z = np.dot(pos, weights) # compute perceptron output
|
||||
if z < 0: # positive example classified as negative
|
||||
weights = weights + eta*weights.shape
|
||||
|
||||
z = np.dot(neg, weights)
|
||||
if z >= 0: # negative example classified as positive
|
||||
weights = weights - eta*weights.shape
|
||||
|
||||
return weights
|
||||
```
|
||||
|
||||
## 結論
|
||||
|
||||
在本課中,你學習了感知器這種二元分類模型,以及如何通過使用權重向量來訓練它。
|
||||
|
||||
## 🚀 挑戰
|
||||
|
||||
如果你想嘗試自己構建一個感知器,可以試試 [Microsoft Learn 的這個實驗](https://docs.microsoft.com/en-us/azure/machine-learning/component-reference/two-class-averaged-perceptron?WT.mc_id=academic-77998-cacaste),它使用了 [Azure ML 設計器](https://docs.microsoft.com/en-us/azure/machine-learning/concept-designer?WT.mc_id=academic-77998-cacaste)。
|
||||
|
||||
## [課後測驗](https://ff-quizzes.netlify.app/en/ai/quiz/6)
|
||||
|
||||
## 回顧與自學
|
||||
|
||||
要了解如何使用感知器解決玩具問題以及實際問題,並繼續學習,請參考 [Perceptron](Perceptron.ipynb) 筆記本。
|
||||
|
||||
這裡還有一篇有趣的[感知器相關文章](https://towardsdatascience.com/what-is-a-perceptron-basics-of-neural-networks-c4cfea20c590)。
|
||||
|
||||
## [作業](lab/README.md)
|
||||
|
||||
在本課中,我們實現了一個用於二元分類任務的感知器,並使用它來區分兩個手寫數字。在這次實驗中,你需要完全解決數字分類問題,也就是確定給定圖像最可能對應的數字。
|
||||
|
||||
* [指導說明](lab/README.md)
|
||||
* [筆記本](lab/PerceptronMultiClass.ipynb)
|
||||
|
||||
---
|
||||
|
||||
File diff suppressed because one or more lines are too long
|
|
@ -0,0 +1,24 @@
|
|||
# 使用感知器進行多類別分類
|
||||
|
||||
來自 [AI for Beginners Curriculum](https://github.com/microsoft/ai-for-beginners) 的實驗作業。
|
||||
|
||||
## 任務
|
||||
|
||||
使用我們在本課中為 MNIST 手寫數字的二元分類所開發的程式碼,創建一個多類別分類器,能夠識別任何數字。計算訓練集和測試集的分類準確率,並輸出混淆矩陣。
|
||||
|
||||
## 提示
|
||||
|
||||
1. 對於每個數字,創建一個二元分類器的數據集,將其設為「該數字 vs. 其他所有數字」
|
||||
1. 訓練 10 個不同的感知器進行二元分類(每個數字一個感知器)
|
||||
1. 定義一個函數來分類輸入的數字
|
||||
|
||||
> **提示**:如果我們將所有 10 個感知器的權重組合成一個矩陣,我們應該能夠通過一次矩陣乘法將所有 10 個感知器應用於輸入數字。最可能的數字可以通過對輸出應用 `argmax` 操作來找到。
|
||||
|
||||
## 起始 Notebook
|
||||
|
||||
通過打開 [PerceptronMultiClass.ipynb](PerceptronMultiClass.ipynb) 開始實驗。
|
||||
|
||||
---
|
||||
|
||||
**免責聲明**:
|
||||
本文件已使用 AI 翻譯服務 [Co-op Translator](https://github.com/Azure/co-op-translator) 進行翻譯。儘管我們致力於提供準確的翻譯,請注意自動翻譯可能包含錯誤或不準確之處。原始文件的母語版本應被視為權威來源。對於關鍵資訊,建議使用專業人工翻譯。我們對因使用此翻譯而引起的任何誤解或錯誤解釋不承擔責任。
|
||||
File diff suppressed because one or more lines are too long
|
|
@ -0,0 +1,89 @@
|
|||
# 神經網路簡介:多層感知器
|
||||
|
||||
在上一節中,你學習了最簡單的神經網路模型——單層感知器,一種線性二分類模型。
|
||||
|
||||
在本節中,我們將擴展這個模型,構建一個更靈活的框架,使我們能夠:
|
||||
|
||||
* 除了二分類之外,還能執行**多分類任務**
|
||||
* 除了分類之外,還能解決**回歸問題**
|
||||
* 區分那些**非線性可分的類別**
|
||||
|
||||
我們還將在 Python 中開發自己的模組化框架,這將使我們能夠構建不同的神經網路架構。
|
||||
|
||||
## [課前測驗](https://ff-quizzes.netlify.app/en/ai/quiz/7)
|
||||
|
||||
## 機器學習的形式化
|
||||
|
||||
讓我們從形式化機器學習問題開始。假設我們有一個訓練數據集 **X** 和標籤 **Y**,我們需要構建一個模型 *f*,以實現最準確的預測。預測的質量由**損失函數** ℒ 來衡量。以下是常用的損失函數:
|
||||
|
||||
* 對於回歸問題(需要預測一個數值),我們可以使用**絕對誤差** ∑<sub>i</sub>|f(x<sup>(i)</sup>)-y<sup>(i)</sup>|,或**平方誤差** ∑<sub>i</sub>(f(x<sup>(i)</sup>)-y<sup>(i)</sup>)<sup>2</sup>
|
||||
* 對於分類問題,我們使用**0-1損失**(本質上與模型的**準確率**相同),或者**邏輯損失**。
|
||||
|
||||
對於單層感知器,函數 *f* 被定義為一個線性函數 *f(x)=wx+b*(其中 *w* 是權重矩陣,*x* 是輸入特徵向量,*b* 是偏置向量)。對於不同的神經網路架構,這個函數可以採用更複雜的形式。
|
||||
|
||||
> 在分類的情況下,通常希望網路輸出對應類別的概率。為了將任意數字轉換為概率(例如對輸出進行歸一化),我們通常使用**softmax 函數** σ,此時函數 *f* 變為 *f(x)=σ(wx+b)*
|
||||
|
||||
在上述 *f* 的定義中,*w* 和 *b* 被稱為**參數** θ=⟨*w,b*⟩。給定數據集 ⟨**X**,**Y**⟩,我們可以將整個數據集上的總體誤差表示為參數 θ 的函數。
|
||||
|
||||
> ✅ **神經網路訓練的目標是通過調整參數 θ 來最小化誤差**
|
||||
|
||||
## 梯度下降優化
|
||||
|
||||
有一種著名的函數優化方法叫做**梯度下降**。其核心思想是,我們可以計算損失函數相對於參數的導數(在多維情況下稱為**梯度**),並調整參數以減少誤差。這可以形式化為以下步驟:
|
||||
|
||||
* 用一些隨機值初始化參數 w<sup>(0)</sup>, b<sup>(0)</sup>
|
||||
* 重複以下步驟多次:
|
||||
- w<sup>(i+1)</sup> = w<sup>(i)</sup>-η∂ℒ/∂w
|
||||
- b<sup>(i+1)</sup> = b<sup>(i)</sup>-η∂ℒ/∂b
|
||||
|
||||
在訓練過程中,優化步驟應該基於整個數據集計算(記住損失是通過所有訓練樣本的總和計算的)。然而,在實際操作中,我們會取數據集的小部分,稱為**小批量(minibatches)**,並基於數據子集計算梯度。由於每次隨機選取子集,這種方法被稱為**隨機梯度下降(SGD)**。
|
||||
|
||||
## 多層感知器與反向傳播
|
||||
|
||||
如上所述,單層網路能夠對線性可分的類別進行分類。為了構建更豐富的模型,我們可以將多層網路結合起來。數學上,這意味著函數 *f* 將具有更複雜的形式,並通過以下幾個步驟計算:
|
||||
* z<sub>1</sub>=w<sub>1</sub>x+b<sub>1</sub>
|
||||
* z<sub>2</sub>=w<sub>2</sub>α(z<sub>1</sub>)+b<sub>2</sub>
|
||||
* f = σ(z<sub>2</sub>)
|
||||
|
||||
這裡,α 是一個**非線性激活函數**,σ 是 softmax 函數,參數為 θ=<*w<sub>1</sub>,b<sub>1</sub>,w<sub>2</sub>,b<sub>2</sub>*>。
|
||||
|
||||
梯度下降算法保持不變,但計算梯度會更加困難。根據鏈式求導法則,我們可以計算導數如下:
|
||||
|
||||
* ∂ℒ/∂w<sub>2</sub> = (∂ℒ/∂σ)(∂σ/∂z<sub>2</sub>)(∂z<sub>2</sub>/∂w<sub>2</sub>)
|
||||
* ∂ℒ/∂w<sub>1</sub> = (∂ℒ/∂σ)(∂σ/∂z<sub>2</sub>)(∂z<sub>2</sub>/∂α)(∂α/∂z<sub>1</sub>)(∂z<sub>1</sub>/∂w<sub>1</sub>)
|
||||
|
||||
> ✅ 鏈式求導法則用於計算損失函數相對於參數的導數。
|
||||
|
||||
注意,所有這些表達式的最左部分是相同的,因此我們可以從損失函數開始,沿著計算圖“向後”有效地計算導數。因此,多層感知器的訓練方法被稱為**反向傳播(backpropagation)**,簡稱“backprop”。
|
||||
|
||||
<img alt="計算圖" src="../../../../../translated_images/zh-TW/ComputeGraphGrad.4626252c0de03507.webp"/>
|
||||
|
||||
> TODO: 圖片引用
|
||||
|
||||
> ✅ 我們將在筆記本範例中更詳細地介紹反向傳播。
|
||||
|
||||
## 結論
|
||||
|
||||
在本課中,我們構建了自己的神經網路庫,並將其用於一個簡單的二維分類任務。
|
||||
|
||||
## 🚀 挑戰
|
||||
|
||||
在隨附的筆記本中,你將實現自己的框架來構建和訓練多層感知器。你將能夠詳細了解現代神經網路的運作方式。
|
||||
|
||||
請前往 [OwnFramework](OwnFramework.ipynb) 筆記本並完成相關內容。
|
||||
|
||||
## [課後測驗](https://ff-quizzes.netlify.app/en/ai/quiz/8)
|
||||
|
||||
## 複習與自學
|
||||
|
||||
反向傳播是人工智慧和機器學習中常用的算法,值得[深入學習](https://wikipedia.org/wiki/Backpropagation)。
|
||||
|
||||
## [作業](lab/README.md)
|
||||
|
||||
在本次實驗中,你需要使用本課中構建的框架來解決 MNIST 手寫數字分類問題。
|
||||
|
||||
* [說明](lab/README.md)
|
||||
* [筆記本](lab/MyFW_MNIST.ipynb)
|
||||
|
||||
---
|
||||
|
||||
|
|
@ -0,0 +1,183 @@
|
|||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"# 使用我們自己的框架進行 MNIST 數字分類\n",
|
||||
"\n",
|
||||
"來自 [AI 初學者課程](https://github.com/microsoft/ai-for-beginners) 的實驗作業。\n",
|
||||
"\n",
|
||||
"### 讀取數據集\n",
|
||||
"\n",
|
||||
"此程式碼從網路上的存儲庫下載數據集。您也可以手動從 AI 課程存儲庫的 `/data` 目錄中複製數據集。\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 4,
|
||||
"metadata": {
|
||||
"tags": []
|
||||
},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stderr",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
" % Total % Received % Xferd Average Speed Time Time Time Current\n",
|
||||
" Dload Upload Total Spent Left Speed\n",
|
||||
"\n",
|
||||
" 0 0 0 0 0 0 0 0 --:--:-- --:--:-- --:--:-- 0\n",
|
||||
"100 9.9M 100 9.9M 0 0 9.9M 0 0:00:01 --:--:-- 0:00:01 15.8M\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"!rm *.pkl\n",
|
||||
"!wget https://raw.githubusercontent.com/microsoft/AI-For-Beginners/main/data/mnist.pkl.gz\n",
|
||||
"!gzip -d mnist.pkl.gz"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 3,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import pickle\n",
|
||||
"with open('mnist.pkl','rb') as f:\n",
|
||||
" MNIST = pickle.load(f)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 4,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"labels = MNIST['Train']['Labels']\n",
|
||||
"data = MNIST['Train']['Features']"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"讓我們看看我們擁有的數據形狀:\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 5,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"(42000, 784)"
|
||||
]
|
||||
},
|
||||
"execution_count": 5,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"data.shape"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"### 分割資料\n",
|
||||
"\n",
|
||||
"我們將使用 Scikit Learn 將資料分成訓練集和測試集:\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 6,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"Train samples: 33600, test samples: 8400\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"from sklearn.model_selection import train_test_split\n",
|
||||
"\n",
|
||||
"features_train, features_test, labels_train, labels_test = train_test_split(data,labels,test_size=0.2)\n",
|
||||
"\n",
|
||||
"print(f\"Train samples: {len(features_train)}, test samples: {len(features_test)}\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"### 指示\n",
|
||||
"\n",
|
||||
"1. 從課程中取得框架代碼,並將其貼到此筆記本中,或(更好)貼到一個獨立的 Python 模組中\n",
|
||||
"1. 定義並訓練單層感知器,觀察訓練和驗證的準確率\n",
|
||||
"1. 嘗試了解是否發生了過度擬合,並調整層的參數以提高準確率\n",
|
||||
"1. 重複前面的步驟,訓練兩層和三層感知器。嘗試在層之間使用不同的激活函數進行實驗\n",
|
||||
"1. 嘗試回答以下問題:\n",
|
||||
" - 層間的激活函數是否影響網絡性能?\n",
|
||||
" - 這個任務是否需要兩層或三層的網絡?\n",
|
||||
" - 在訓練網絡時是否遇到任何問題?特別是在層數增加時。\n",
|
||||
" - 網絡的權重在訓練過程中如何表現?你可以繪製權重的最大絕對值與訓練輪次的關係圖來理解這種關係。\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": []
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"\n---\n\n**免責聲明**: \n本文件使用 AI 翻譯服務 [Co-op Translator](https://github.com/Azure/co-op-translator) 進行翻譯。我們致力於提供準確的翻譯,但請注意,自動翻譯可能包含錯誤或不準確之處。應以原文文件作為權威來源。對於關鍵資訊,建議尋求專業人工翻譯。我們對因使用此翻譯而引起的任何誤解或誤讀概不負責。\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"kernelspec": {
|
||||
"display_name": "Python 3.7.4 64-bit (conda)",
|
||||
"metadata": {
|
||||
"interpreter": {
|
||||
"hash": "86193a1ab0ba47eac1c69c1756090baa3b420b3eea7d4aafab8b85f8b312f0c5"
|
||||
}
|
||||
},
|
||||
"name": "python3"
|
||||
},
|
||||
"language_info": {
|
||||
"codemirror_mode": {
|
||||
"name": "ipython",
|
||||
"version": 3
|
||||
},
|
||||
"file_extension": ".py",
|
||||
"mimetype": "text/x-python",
|
||||
"name": "python",
|
||||
"nbconvert_exporter": "python",
|
||||
"pygments_lexer": "ipython3",
|
||||
"version": "3.9.5"
|
||||
},
|
||||
"orig_nbformat": 2,
|
||||
"coopTranslator": {
|
||||
"original_hash": "6fa055f484eb5d6bdf41166a356d3abf",
|
||||
"translation_date": "2025-08-31T10:12:50+00:00",
|
||||
"source_file": "lessons/3-NeuralNetworks/04-OwnFramework/lab/MyFW_MNIST.ipynb",
|
||||
"language_code": "tw"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 2
|
||||
}
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
# 使用我們自己的框架進行 MNIST 分類
|
||||
|
||||
來自 [AI for Beginners Curriculum](https://github.com/microsoft/ai-for-beginners) 的實驗作業。
|
||||
|
||||
## 任務
|
||||
|
||||
使用 1 層、2 層和 3 層的感知器解決 MNIST 手寫數字分類問題。使用我們在課程中開發的神經網路框架。
|
||||
|
||||
## 啟動 Notebook
|
||||
|
||||
通過打開 [MyFW_MNIST.ipynb](../../../../../../lessons/3-NeuralNetworks/04-OwnFramework/lab/MyFW_MNIST.ipynb) 開始實驗。
|
||||
|
||||
## 問題
|
||||
|
||||
完成本次實驗後,嘗試回答以下問題:
|
||||
|
||||
- 層間的激活函數是否會影響網路的性能?
|
||||
- 這個任務是否需要 2 層或 3 層的網路?
|
||||
- 在訓練網路時是否遇到任何問題?特別是當層數增加時。
|
||||
- 網路的權重在訓練過程中是如何變化的?您可以繪製權重最大絕對值與 epoch 的關係圖來理解這種關係。
|
||||
|
||||
**免責聲明**:
|
||||
本文件使用 AI 翻譯服務 [Co-op Translator](https://github.com/Azure/co-op-translator) 進行翻譯。我們致力於提供準確的翻譯,但請注意,自動翻譯可能包含錯誤或不準確之處。應以原始語言的文件作為權威來源。對於關鍵資訊,建議尋求專業人工翻譯。我們對於因使用此翻譯而引起的任何誤解或誤讀概不負責。
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
Loading…
Reference in New Issue