Python: Python AI: Cách xây dựng mạng Nơ-ron thần kinh và đưa ra dự đoán
Mục lục bài viết:
Nếu bạn mới bắt đầu tham gia vào thế giới trí tuệ nhân tạo (AI), thì Python là một ngôn ngữ tuyệt vời để học vì hầu hết các công cụ đều được xây dựng bằng cách sử dụng nó. Học sâu (Deep learning) là một kỹ thuật được sử dụng để đưa ra dự đoán bằng cách sử dụng dữ liệu và nó chủ yếu dựa vào mạng nơ-ron. Trong bài học này, bạn sẽ học cách xây dựng một mạng nơ-ron từ đầu.
Trong cài đặt production, bạn sẽ sử dụng một framework học sâu như TensorFlow hoặc PyTorch thay vì xây dựng mạng nơ-ron của riêng bạn. Điều đó nói rằng, có một số kiến thức về cách mạng nơ-ron hoạt động sẽ rất hữu ích vì bạn có thể sử dụng nó để kiến trúc tốt hơn các mô hình học sâu của mình.
Nội dung chính của bài học:
- Trí tuệ nhân tạo (Artificial Intelligence - AI) là gì
- Học máy (Machine Learning - ML) và học sâu (Deep Learning - DL) đóng vai trò như thế nào đối với AI
- Cách mạng nơ-ron hoạt động bên trong
- Cách xây dựng mạng nơ-ron từ đầu bằng Python
Bắt đầu nào!
Tổng quan về trí tuệ nhân tạo
Về cơ bản, mục tiêu của việc sử dụng AI là làm cho máy tính suy nghĩ như con người. Đây có vẻ là một điều gì đó mới mẻ, nhưng lĩnh vực này đã ra đời từ những năm 1950.
Hãy tưởng tượng rằng bạn cần viết một chương trình Python sử dụng AI để giải quyết một vấn đề sudoku. Một cách để thực hiện điều đó là viết các câu lệnh điều kiện và kiểm tra các ràng buộc để xem liệu bạn có thể đặt một số ở mỗi vị trí hay không. Như vậy thì tập lệnh Python này đã là một ứng dụng của AI vì bạn đã lập trình máy tính để giải quyết một vấn đề!
Học máy (ML) và học sâu (DL) cũng là những cách tiếp cận để giải quyết vấn đề. Sự khác biệt giữa các kỹ thuật này và tập lệnh Python là ML và DL sử dụng dữ liệu huấn luyện thay vì các quy tắc được mã hóa cứng, nhưng tất cả chúng đều có thể được sử dụng để giải quyết vấn đề bằng cách sử dụng AI. Trong các phần tiếp theo, bạn sẽ tìm hiểu thêm về điểm khác biệt của hai kỹ thuật này.
Học máy (Machine Learning)
Học máy là một kỹ thuật trong đó bạn huấn luyện hệ thống để giải quyết một vấn đề thay vì lập trình các quy tắc một cách rõ ràng. Quay lại với ví dụ sudoku trong phần trước, để giải quyết vấn đề bằng cách sử dụng học máy, bạn sẽ thu thập dữ liệu từ các trò chơi sudoku đã giải và đào tạo một mô hình thống kê. Các mô hình thống kê là những cách được chính thức hóa về mặt toán học để ước tính gần đúng hành vi của một hiện tượng.
Một tác vụ học máy phổ biến là học có giám sát, trong đó bạn có một tập dữ liệu với các đầu vào và đầu ra đã biết. Nhiệm vụ là sử dụng tập dữ liệu này để đào tạo một mô hình dự đoán các kết quả đầu ra chính xác dựa trên các đầu vào. Hình ảnh dưới đây trình bày quy trình làm việc để đào tạo một mô hình sử dụng phương pháp học có giám sát:
Quy trình làm việc để đào tạo một mô hình học máy
Sự kết hợp của dữ liệu đào tạo với thuật toán học máy tạo ra mô hình. Sau đó, với mô hình này, bạn có thể đưa ra dự đoán cho dữ liệu mới.
Lưu ý: scikit-learning là một thư viện học máy Python phổ biến cung cấp nhiều thuật toán học có giám sát và không giám sát. Để tìm hiểu thêm về nó, hãy xem Tách tập dữ liệu của bạn với train_test_split () của scikit-learning.
Mục tiêu của các nhiệm vụ học tập có giám sát là đưa ra dự đoán cho dữ liệu mới, chưa nhìn thấy. Để làm điều đó, bạn giả sử rằng dữ liệu không nhìn thấy này tuân theo phân phối xác suất tương tự như phân phối của tập dữ liệu đào tạo. Nếu trong tương lai, phân phối này thay đổi, thì bạn cần đào tạo lại mô hình của mình bằng cách sử dụng tập dữ liệu đào tạo mới.
Đặc điểm kỹ thuật
Các vấn đề dự đoán trở nên khó hơn khi bạn sử dụng các loại dữ liệu khác nhau làm đầu vào. Vấn đề sudoku tương đối đơn giản vì bạn đang giải quyết trực tiếp với các con số. Điều gì sẽ xảy ra nếu bạn muốn đào tạo một mô hình để dự đoán tình cảm trong một câu? Hoặc nếu bạn có một hình ảnh và bạn muốn biết liệu nó có mô tả một con mèo hay không?
Một tên khác của dữ liệu đầu vào là tính năng (reature) và kỹ thuật tính năng (feature engineering) là quá trình trích xuất các tính năng từ dữ liệu thô. Khi xử lý các loại dữ liệu khác nhau, bạn cần tìm ra các cách biểu diễn dữ liệu này để trích xuất thông tin có ý nghĩa từ nó.
Một ví dụ về kỹ thuật thiết kế tính năng là lemmatization, trong đó bạn loại bỏ phần uốn cong khỏi các từ trong một câu. Ví dụ: các dạng suy luận của động từ "watch" như là "watches", "watching" và "watched", sẽ được rút gọn thành bổ đề hoặc dạng cơ sở: "watch".
Nếu bạn đang sử dụng mảng để lưu trữ từng từ của một kho ngữ liệu, thì bằng cách áp dụng bổ sung, bạn sẽ có một ma trận ít thưa thớt hơn. Điều này có thể làm tăng hiệu suất của một số thuật toán học máy. Hình ảnh sau đây trình bày quá trình lemmatization và biểu diễn bằng mô hình bag-of-words:
Tạo các đối tượng địa lý bằng mô hình bag-of-words
Đầu tiên, dạng suy luận của mọi từ được rút gọn thành bổ đề của nó. Sau đó, số lần xuất hiện của từ đó được tính. Kết quả là một mảng chứa số lần xuất hiện của mỗi từ trong văn bản.
Học sâu (Deep Learning - DL)
Học sâu là một kỹ thuật trong đó bạn để mạng nơ-ron tự tìm ra các tính năng nào là quan trọng thay vì áp dụng tính năng kỹ thuật kỹ thuật. Điều này có nghĩa là, với học sâu, bạn có thể bỏ qua quy trình kỹ thuật tính năng.
Không hẳn xử lý kỹ thuật tính năng là tốt vì quá trình này trở nên khó khăn hơn khi các bộ dữ liệu trở nên phức tạp hơn. Ví dụ, bạn sẽ trích xuất dữ liệu như thế nào để dự đoán tâm trạng của một người được đưa cho một bức ảnh về khuôn mặt của cô ấy? Với mạng nơ-ron, bạn không cần phải lo lắng về điều đó vì các mạng có thể tự học các tính năng. Trong các phần tiếp theo, bạn sẽ đi sâu vào các mạng nơ-ron để hiểu rõ hơn về cách chúng hoạt động.
Mạng nơ-ron: Các khái niệm chính
Mạng nơ-ron là một hệ thống học cách đưa ra dự đoán bằng cách làm theo các bước sau:
- Lấy dữ liệu đầu vào
- Đưa ra dự đoán
- So sánh dự đoán với kết quả mong muốn
- Điều chỉnh trạng thái bên trong của nó để dự đoán chính xác trong lần tiếp theo
Vectơ, lớp (layer) và hồi quy tuyến tính là một số nền tảng của mạng nơ-ron. Dữ liệu được lưu trữ dưới dạng vectơ và với Python, bạn lưu trữ các vectơ này trong mảng. Mỗi lớp chuyển đổi dữ liệu đến từ lớp trước đó. Bạn có thể coi mỗi lớp như một bước kỹ thuật tính năng, bởi vì mỗi lớp trích xuất một số đại diện của dữ liệu đã có trước đó.
Một điều thú vị về các lớp mạng nơ-ron là các phép tính giống nhau có thể trích xuất thông tin từ bất kỳ loại dữ liệu nào. Điều này có nghĩa là bạn đang sử dụng dữ liệu hình ảnh hay dữ liệu văn bản đều không thành vấn đề. Quá trình trích xuất thông tin có ý nghĩa và đào tạo mô hình học sâu là giống nhau đối với cả hai tình huống.
Trong hình ảnh bên dưới, bạn có thể thấy một ví dụ về kiến trúc mạng có hai lớp:
Mỗi lớp chuyển đổi dữ liệu đến từ lớp trước bằng cách áp dụng một số phép toán.
Quy trình để đào tạo một mạng nơ-ron
Đào tạo một mạng nơ-ron tương tự như quá trình thử và sai. Hãy tưởng tượng bạn đang chơi phi tiêu lần đầu tiên. Trong lần ném đầu tiên, bạn cố gắng ném trúng điểm chính giữa của bảng phi tiêu. Thông thường, cú đánh đầu tiên chỉ là để biết chiều cao và tốc độ của tay bạn ảnh hưởng như thế nào đến kết quả. Nếu bạn thấy phi tiêu cao hơn điểm trung tâm, thì bạn điều chỉnh tay để ném nó xuống thấp hơn một chút, cứ tiếp tục như vậy.
Đây là các bước để cố gắng bắn trúng tâm của bảng phi tiêu:
Các bước để ném phi tiêu vào tâm
Lưu ý rằng bạn tiếp tục đánh giá lỗi bằng cách quan sát nơi phi tiêu hạ cánh (bước 2). Bạn tiếp tục cho đến khi cuối cùng bạn chạm vào tâm của bảng phi tiêu.
Với mạng nơ-ron, quá trình này rất tương tự: bạn bắt đầu với một số trọng số ngẫu nhiên và vectơ thiên vị, đưa ra dự đoán, so sánh nó với đầu ra mong muốn và điều chỉnh các vectơ để dự đoán chính xác hơn trong lần tiếp theo. Quá trình tiếp tục cho đến khi sự khác biệt giữa dự đoán và các mục tiêu chính xác là nhỏ nhất.
Biết khi nào nên dừng đào tạo và mục tiêu chính xác cần đặt là một khía cạnh quan trọng của việc đào tạo mạng nơ-ron, chủ yếu là do các tình huống trang bị quá mức và trang bị thấp.
Vectơ và trọng số
Làm việc với mạng nơron bao gồm thực hiện các hoạt động với vectơ. Bạn biểu diễn các vectơ dưới dạng mảng nhiều chiều. Các vectơ hữu ích trong học sâu chủ yếu nhờ vào một hoạt động cụ thể: sản phẩm dấu chấm. Tích số chấm của hai vectơ cho bạn biết chúng giống nhau như thế nào về hướng và được chia tỷ lệ theo độ lớn của hai vectơ.
Các vectơ chính bên trong một mạng nơron là các vectơ trọng số và thiên vị. Nói một cách lỏng lẻo, những gì bạn muốn mạng nơ-ron của mình làm là kiểm tra xem một đầu vào có giống với các đầu vào khác mà nó đã thấy hay không. Nếu đầu vào mới tương tự với các đầu vào đã thấy trước đây, thì các đầu ra cũng sẽ tương tự. Đó là cách bạn nhận được kết quả của một dự đoán.
Mô hình hồi quy tuyến tính
Hồi quy được sử dụng khi bạn cần ước tính mối quan hệ giữa một biến phụ thuộc và hai hoặc nhiều biến độc lập. Hồi quy tuyến tính là một phương pháp được áp dụng khi bạn ước tính mối quan hệ giữa các biến là tuyến tính. Phương pháp này có từ thế kỷ XIX và là phương pháp hồi quy phổ biến nhất.
Lưu ý: Một mối quan hệ tuyến tính là một trong những nơi có một mối quan hệ trực tiếp giữa biến độc lập và biến phụ thuộc.
Bằng cách mô hình hóa mối quan hệ giữa các biến dưới dạng tuyến tính, bạn có thể biểu thị biến phụ thuộc dưới dạng tổng có trọng số của các biến độc lập. Vì vậy, mỗi biến độc lập sẽ được nhân với một vector được gọi weight
. Bên cạnh trọng số và các biến độc lập, bạn cũng thêm một vectơ khác: độ lệch. Nó đặt kết quả khi tất cả các biến độc lập khác bằng không.
Là một ví dụ thực tế về cách xây dựng mô hình hồi quy tuyến tính, hãy tưởng tượng bạn muốn đào tạo một mô hình để dự đoán giá nhà dựa trên diện tích và độ cũ của ngôi nhà. Bạn quyết định mô hình hóa mối quan hệ này bằng cách sử dụng hồi quy tuyến tính. Khối mã sau đây cho thấy cách bạn có thể viết mô hình hồi quy tuyến tính cho vấn đề đã nêu bằng mã giả:
price = (weights_area * area) + (weights_age * age) + bias
Trong ví dụ trên, có hai trọng số: weights_area
và weights_age
. Quá trình đào tạo bao gồm điều chỉnh trọng số và độ lệch để mô hình có thể dự đoán giá trị chính xác. Để thực hiện điều đó, bạn sẽ cần tính toán lỗi dự đoán và cập nhật trọng số cho phù hợp.
Đây là những điều cơ bản về cách hoạt động của cơ chế mạng nơ-ron. Bây giờ là lúc để xem cách áp dụng các khái niệm này bằng Python.
Python AI: Bắt đầu xây dựng mạng nơron đầu tiên của bạn
Bước đầu tiên trong việc xây dựng mạng nơ-ron là tạo đầu ra từ dữ liệu đầu vào. Bạn sẽ làm điều đó bằng cách tạo một tổng trọng số của các biến. Điều đầu tiên bạn cần làm là biểu diễn các đầu vào bằng Python và NumPy.
Gói các đầu vào của mạng nơ-ron với NumPy
Bạn sẽ sử dụng NumPy để biểu diễn các vectơ đầu vào của mạng dưới dạng mảng. Nhưng trước khi sử dụng NumPy, bạn nên thử với các vectơ trong Python thuần túy để hiểu rõ hơn điều gì đang xảy ra.
Trong ví dụ đầu tiên này, bạn có một vectơ đầu vào và hai vectơ trọng số còn lại. Mục đích là tìm trọng lượng nào giống với đầu vào hơn, có tính đến hướng và độ lớn. Đây là cách các vectơ trông như thế nào nếu bạn vẽ chúng:
Ba vectơ trong một mặt phẳng tọa độ Đề-các (Descartes)
weights_2
tương tự hơn với vectơ đầu vào vì nó chỉ theo cùng một hướng và độ lớn cũng tương tự. Vậy làm cách nào để tìm ra các vectơ nào tương tự nhau bằng Python?
Đầu tiên, bạn xác định ba vectơ, một cho đầu vào và hai cho trọng số. Sau đó, bạn tính toán mức độ tương tự input_vector
và weights_1
. Để làm điều đó, bạn sẽ áp dụng sản phẩm chấm. Vì tất cả các vectơ đều là vectơ hai chiều, nên đây là các bước để thực hiện điều đó:
- Nhân chỉ số đầu tiên của
input_vector
với chỉ số đầu tiên củaweights_1
. - Nhân chỉ số thứ hai của
input_vector
với chỉ số thứ hai củaweights_2
. - Tính tổng kết quả của cả hai phép nhân.
Bạn có thể sử dụng bảng điều khiển IPython hoặc Máy tính xách tay Jupyter để làm theo. Bạn nên tạo một môi trường ảo mới mỗi khi bạn bắt đầu một dự án Python mới, vì vậy bạn nên làm điều đó trước. venv đi kèm với phiên bản Python 3.3 trở lên và rất tiện lợi để tạo môi trường ảo:
$ python -m venv ~/.my-env
$ source ~/.my-env/bin/activate
Để sử dụng các lệnh trên, trước tiên bạn tạo môi trường ảo, sau đó bạn kích hoạt nó. Bây giờ đã đến lúc cài đặt bảng điều khiển IPython bằng cách sử dụng pip. Vì bạn cũng sẽ cần NumPy và Matplotlib, nên bạn cũng nên cài đặt chúng:
(my-env) $ python -m pip install ipython numpy matplotlib
(my-env) $ ipython
Bây giờ bạn đã sẵn sàng để bắt đầu viết mã. Đây là mã để tính toán sản phẩm chấm của input_vector
và weights_1
:
In [1]: input_vector = [1.72, 1.23]
In [2]: weights_1 = [1.26, 0]
In [3]: weights_2 = [2.17, 0.32]
In [4]: # Computing the dot product of input_vector and weights_1
In [5]: first_indexes_mult = input_vector[0] * weights_1[0]
In [6]: second_indexes_mult = input_vector[1] * weights_1[1]
In [7]: dot_product_1 = first_indexes_mult + second_indexes_mult
In [8]: print(f"The dot product is: {dot_product_1}")
Out[8]: The dot product is: 2.1672
Kết quả của sản phẩm chấm là 2.1672
. Bây giờ bạn đã biết cách tính toán sản phẩm chấm, đã đến lúc sử dụng np.dot()
từ NumPy. Đây là cách tính toán dot_product_1
bằng cách sử dụng np.dot()
:
In [9]: import numpy as np
In [10]: dot_product_1 = np.dot(input_vector, weights_1)
In [11]: print(f"The dot product is: {dot_product_1}")
Out[11]: The dot product is: 2.1672
np.dot()
thực hiện tương tự như bạn đã làm trước đây, nhưng bây giờ bạn chỉ cần chỉ định hai mảng làm đối số. Bây giờ, hãy tính tích số chấm của input_vector
và weights_2
:
In [10]: dot_product_2 = np.dot(input_vector, weights_2)
In [11]: print(f"The dot product is: {dot_product_2}")
Out[11]: The dot product is: 4.1259
Lần này, kết quả là 4.1259
. Là một cách suy nghĩ khác về sản phẩm dấu chấm, bạn có thể coi sự giống nhau giữa các tọa độ vectơ như một công tắc bật-tắt. Nếu kết quả phép nhân là 0
, thì bạn sẽ nói rằng các tọa độ không tương tự. Nếu kết quả là một cái gì đó khác 0
, thì bạn sẽ nói rằng chúng là tương tự.
Bằng cách này, bạn có thể xem sản phẩm chấm như một phép đo lỏng lẻo về độ giống nhau giữa các vectơ. Mỗi khi kết quả của phép nhân là 0
thì tích cuối cùng sẽ có kết quả thấp hơn. Quay lại các vectơ của ví dụ, vì tích số chấm của input_vector
và weights_2
là 4.1259
, và 4.1259
lớn hơn 2.1672
, điều đó có nghĩa input_vector
là giống với weights_2
. Bạn sẽ sử dụng cơ chế tương tự trong mạng nơ-ron của mình.
Trong hướng dẫn này, bạn sẽ đào tạo một mô hình để đưa ra các dự đoán chỉ có hai kết quả có thể xảy ra. Kết quả đầu ra có thể là 0
hoặc 1
. Đây là một bài toán phân loại, một tập con của các bài toán học có giám sát, trong đó bạn có một tập dữ liệu với các đầu vào và các mục tiêu đã biết. Đây là các đầu vào và đầu ra của tập dữ liệu:
Véc tơ đầu vào | Mục tiêu |
---|---|
[1,66, 1,56] | 1 |
[2, 1,5] | 0 |
Các mục tiêu là biến bạn muốn dự đoán. Trong ví dụ này, bạn đang xử lý một tập dữ liệu bao gồm các số. Điều này không phổ biến trong một kịch bản sản phẩm thực. Thông thường, khi cần một mô hình học sâu, dữ liệu được trình bày dưới dạng tệp, chẳng hạn như hình ảnh hoặc văn bản.
Đưa ra dự đoán đầu tiên của bạn
Vì đây là mạng nơ-ron đầu tiên của bạn, bạn sẽ giữ mọi thứ đơn giản và xây dựng một mạng chỉ có hai lớp. Bạn đã thấy rằng hai phép toán duy nhất được sử dụng bên trong mạng nơ-ron là tích số chấm và tính tổng. Cả hai đều là các phép toán tuyến tính.
Nếu bạn thêm nhiều lớp hơn nhưng tiếp tục chỉ sử dụng các phép toán tuyến tính, thì việc thêm nhiều lớp hơn sẽ không có tác dụng vì mỗi lớp sẽ luôn có một số mối tương quan với đầu vào của lớp trước đó. Điều này ngụ ý rằng, đối với một mạng có nhiều lớp, sẽ luôn có một mạng có ít lớp hơn dự đoán cùng một kết quả.
Có thể bạn muốn là tìm một phép toán thực thi cho các lớp giữa đôi khi tương quan với một đầu vào và đôi khi không tương quan.
Bạn có thể đạt được hành vi này bằng cách sử dụng các hàm phi tuyến. Các hàm phi tuyến này được gọi là các hàm kích hoạt. Có nhiều loại hàm kích hoạt. Các ReLU (Rectified Linear Unit), ví dụ, là một chức năng chuyển đổi tất cả số âm thành không. Điều này có nghĩa là mạng có thể "tắt" trọng số nếu nó âm, làm tăng thêm tính phi tuyến.
Mạng bạn đang xây dựng sẽ sử dụng chức năng kích hoạt sigmoid. Bạn sẽ sử dụng nó trong lớp cuối cùng là layer_2
. Hai đầu ra duy nhất có thể có trong tập dữ liệu là 0
và 1
, và hàm sigmoid giới hạn đầu ra trong phạm vi giữa 0
và 1
. Đây là công thức để thể hiện hàm sigmoid:
e là một hằng số toán học được gọi là số Euler, và bạn có thể sử dụng np.exp(x)
để tính toán ex.
Các hàm xác suất cung cấp cho bạn xác suất xảy ra đối với các kết quả có thể xảy ra của một sự kiện. Hai kết quả đầu ra duy nhất có thể có của tập dữ liệu là 0
và 1
, và phân phối Bernoulli cũng là một phân phối có hai kết quả có thể xảy ra. Hàm sigmoid là một lựa chọn tốt nếu vấn đề của bạn tuân theo phân phối Bernoulli, vì vậy đó là lý do tại sao bạn đang sử dụng nó trong lớp cuối cùng của mạng nơ-ron của mình.
Vì hàm giới hạn đầu ra trong phạm vi 0
đến 1
, bạn sẽ sử dụng nó để dự đoán xác suất. Nếu đầu ra lớn hơn 0.5
, thì bạn sẽ dự đoán là 1
. Nếu nó nhỏ hơn 0.5
, thì bạn sẽ dự đoán là 0
.
Dưới đây là luồng tính toán bên trong mạng mà bạn đang xây dựng:
Luồng tính toán bên trong mạng nơ-ron của bạn
Các hình lục giác màu vàng đại diện cho các hàm và các hình chữ nhật màu xanh đại diện cho các kết quả trung gian. Bây giờ đã đến lúc chuyển tất cả kiến thức này thành code. Bạn cũng sẽ cần gộp các vectơ lại bằng mảng NumPy. Đây là code áp dụng các hàm được trình bày trong hình trên:
In [12]: # Wrapping the vectors in NumPy arrays
In [13]: input_vector = np.array([1.66, 1.56])
In [14]: weights_1 = np.array([1.45, -0.66])
In [15]: bias = np.array([0.0])
In [16]: def sigmoid(x):
...: return 1 / (1 + np.exp(-x))
In [17]: def make_prediction(input_vector, weights, bias):
...: layer_1 = np.dot(input_vector, weights) + bias
...: layer_2 = sigmoid(layer_1)
...: return layer_2
In [18]: prediction = make_prediction(input_vector, weights_1, bias)
In [19]: print(f"The prediction result is: {prediction}")
Out[19]: The prediction result is: [0.7985731]
Kết quả dự đoán thô 0.79
là cao hơn 0.5
, nên kết quả đầu ra 1
. Mạng đã đưa ra một dự đoán chính xác. Bây giờ hãy thử nó với một vector đầu vào khác là np.array([2, 1.5])
. Kết quả chính xác cho đầu vào này là 0
. Bạn sẽ chỉ cần thay đổi biến input_vector
vì tất cả các tham số khác vẫn như cũ:
In [20]: # Changing the value of input_vector
In [21]: input_vector = np.array([2, 1.5])
In [22]: prediction = make_prediction(input_vector, weights_1, bias)
In [23]: print(f"The prediction result is: {prediction}")
Out[23]: The prediction result is: [0.87101915]
Lần này, mạng đã dự đoán sai. Kết quả phải nhỏ hơn 0.5
vì mục tiêu cho đầu vào này là 0
, nhưng kết quả thô là 0.87
. Nó đã đoán sai, nhưng sai lầm tồi tệ đến mức nào? Bước tiếp theo là tìm cách đánh giá điều đó.
Đào tạo mạng nơ-ron đầu tiên của bạn
Trong quá trình luyện mạng nơ-ron, đầu tiên bạn đánh giá lỗi và sau đó điều chỉnh trọng số cho phù hợp. Để điều chỉnh trọng số, bạn sẽ sử dụng các thuật toán chiết xuất gradient và lan truyền ngược. Gradient descent được áp dụng để tìm hướng và tốc độ cập nhật các thông số.
Trước khi thực hiện bất kỳ thay đổi nào trong mạng, bạn cần tính toán lỗi. Đó là những gì bạn sẽ làm trong phần tiếp theo.
Tính toán lỗi dự đoán
Để hiểu mức độ của sai số, bạn cần chọn một cách để đo lường nó. Hàm được sử dụng để đo lỗi được gọi là hàm chi phí, hoặc hàm tổn thất. Trong hướng dẫn này, bạn sẽ sử dụng lỗi bình phương trung bình (MSE) làm hàm chi phí của mình. Bạn tính toán MSE theo hai bước:
- Tính toán sự khác biệt giữa dự đoán và mục tiêu.
- Nhân kết quả với chính nó.
Mạng có thể mắc lỗi khi xuất giá trị cao hơn hoặc thấp hơn giá trị chính xác. Vì MSE là chênh lệch bình phương giữa dự đoán và kết quả chính xác, nên với số liệu này, bạn sẽ luôn nhận được một giá trị dương.
Đây là biểu thức đầy đủ để tính toán lỗi cho dự đoán trước đó cuối cùng:
In [24]: target = 0
In [25]: mse = np.square(prediction - target)
In [26]: print(f"Prediction: {prediction}; Error: {mse}")
Out[26]: Prediction: [0.87101915]; Error: [0.7586743596667225]
Trong ví dụ trên, lỗi là 0.75
. Một hàm ý của việc nhân sự khác biệt với chính nó là các lỗi lớn hơn có tác động lớn hơn và các lỗi nhỏ hơn tiếp tục nhỏ hơn khi chúng giảm.
Hiểu cách giảm thiểu lỗi
Mục đích là thay đổi trọng số và biến thiên vị để bạn có thể giảm lỗi. Để hiểu cách thức hoạt động của điều này, bạn sẽ chỉ thay đổi biến trọng số và giữ nguyên độ lệch vào lúc này. Bạn cũng có thể loại bỏ hàm sigmoid và chỉ sử dụng kết quả của layer_1
. Tất cả những gì còn lại là tìm ra cách bạn có thể sửa đổi trọng số để lỗi giảm xuống.
Bạn tính toán MSE bằng cách thực hiện error = np.square(prediction - target)
. Nếu bạn đặt (prediction - target)
là x
, thì bạn có error = np.square(x)
, đó là một hàm bậc hai. Đây là cách hàm trông như thế nào nếu bạn vẽ biểu đồ của nó:
Lỗi được cung cấp bởi trục y. Nếu bạn đang ở đúng điểm A
và muốn giảm lỗi về 0, thì bạn cần phải hạ giá trị x
xuống. Mặt khác, nếu bạn đang quan tâm đến B
và muốn giảm lỗi, thì bạn cần phải nâng giá trị x
lên. Để biết bạn nên đi theo hướng nào để giảm lỗi, bạn sẽ sử dụng đạo hàm. Đạo hàm sẽ giải thích chính xác cách một mẫu sẽ thay đổi.
Một từ khác cho đạo hàm là gradient. Gradient descent là tên của thuật toán được sử dụng để tìm hướng và tốc độ cập nhật các thông số mạng.
Lưu ý: Để tìm hiểu thêm về toán học ẩn sau gradient descent, hãy xem Thuật toán Stochastic Gradient Descent với Python và NumPy.
Trong hướng dẫn này, bạn sẽ không tập trung vào lý thuyết đằng sau các đạo hàm, vì vậy bạn chỉ cần áp dụng các quy tắc đạo hàm cho mỗi hàm bạn sẽ gặp, ví dụ như đạo hàm của xⁿ là nx ⁽ ⁿ ⁻¹⁾. Vì vậy, đạo hàm của np.square(x)
là 2 * x
, và đạo hàm của x
là 1
.
Hãy nhớ rằng biểu thức lỗi là error = np.square(prediction - target)
. Khi bạn xử lý (prediction - target)
như một biến duy nhất x
, thì đạo hàm của lỗi là 2 * x
. Bằng cách lấy đạo hàm của hàm này, bạn muốn biết bạn nên thay đổi theo hướng nào để x
đưa kết quả về error
0, từ đó giảm sai số.
Khi nói đến mạng nơron của bạn, đạo hàm sẽ cho bạn biết hướng bạn nên thực hiện để cập nhật biến trọng số. Nếu đó là một số dương, thì bạn đã dự đoán quá cao và bạn cần giảm trọng số. Nếu đó là một số âm, thì bạn đã dự đoán quá thấp và bạn cần tăng trọng số.
Bây giờ là lúc viết code để tìm ra cách cập nhật weights_1
cho dự đoán sai ở trên. Nếu sai số bình phương trung bình là 0.75
, thì bạn nên tăng hay giảm trọng số? Vì là đạo hàm 2 * x
, nên bạn chỉ cần nhân chênh lệch giữa dự đoán và mục tiêu với 2
:
In [27]: derivative = 2 * (prediction - target)
In [28]: print(f"The derivative is {derivative}")
Out[28]: The derivative is: [1.7420383]
Kết quả là 1.74
, một số dương, vì vậy bạn cần giảm trọng số. Bạn làm điều đó bằng cách trừ kết quả đạo hàm của vectơ trọng số. Bây giờ bạn có thể cập nhật weights_1
cho phù hợp và dự đoán lại để xem nó ảnh hưởng như thế nào đến kết quả dự đoán:
In [29]: # Updating the weights
In [30]: weights_1 = weights_1 - derivative
In [31]: prediction = make_prediction(input_vector, weights_1, bias)
In [32]: error = (prediction - target) ** 2
In [33]: print(f"Prediction: {prediction}; Error: {error}")
Out[33]: Prediction: [0.01496248]; Error: [0.00022388]
Lỗi giảm xuống gần như là bằng 0
! Đẹp đúng không? Trong ví dụ này, kết quả đạo hàm là nhỏ, nhưng có một số trường hợp kết quả đạo hàm quá cao. Lấy hình ảnh của hàm số bậc hai làm ví dụ. Mức tăng cao không phải là lý tưởng vì bạn có thể tiếp tục đi thằng từ điểm A
đến điểm B
, không thể tiến gần đến số không. Để xử lý điều này, bạn cập nhật trọng số với một phần nhỏ của kết quả đạo hàm.
Để định nghĩa một phân số để cập nhật trọng số, bạn sử dụng tham số alpha, còn được gọi là tỷ lệ học. Nếu bạn giảm tốc độ học tập, thì mức tăng sẽ nhỏ hơn. Nếu bạn tăng nó, thì mức tăng sẽ cao hơn. Làm thế nào để bạn biết giá trị tỷ lệ học tập tốt nhất là gì? Câu trả lời là bằng cách phỏng đoán và thử nghiệm nó.
Lưu ý: giá trị tỷ lệ học mặc định là 0.1, 0.01 và 0.001.
Nếu bạn lấy các trọng số mới và đưa ra dự đoán với vectơ đầu vào đầu tiên, thì bạn sẽ thấy rằng bây giờ nó đưa ra dự đoán sai cho vectơ đó. Nếu mạng nơ-ron của bạn đưa ra dự đoán chính xác cho mọi trường hợp trong tập huấn luyện của bạn, thì bạn có thể có một mô hình được trang bị quá mức, trong đó mô hình chỉ ghi nhớ cách phân loại các ví dụ thay vì học cách chú ý đến các tính năng trong dữ liệu.
Có những kỹ thuật để tránh điều đó, bao gồm cả việc chính tắc hóa đối với stochastic gradient descent. Trong hướng dẫn này, bạn sẽ sử dụng stochastic gradient descent online.
Bây giờ bạn đã biết cách tính toán lỗi và cách điều chỉnh trọng số cho phù hợp, đã đến lúc quay lại tiếp tục xây dựng mạng nơ-ron của bạn.
Áp dụng quy tắc chuỗi
Trong mạng nơ-ron của mình, bạn cần cập nhật cả trọng số và vectơ thiên vị. Hàm bạn đang sử dụng để đo lỗi phụ thuộc vào hai biến độc lập, trọng số và độ lệch. Vì trọng số và độ lệch là các biến độc lập, nên bạn có thể thay đổi và điều chỉnh chúng để có được kết quả mong muốn.
Mạng bạn đang xây dựng có hai lớp và vì mỗi lớp có các chức năng riêng của nó, nên bạn đang xử lý một thành phần hàm. Điều này có nghĩa là hàm lỗi vẫn là np.square(x)
, nhưng bây giờ x
là kết quả của một hàm khác.
Để khắc phục sự cố, bây giờ bạn muốn biết cách thay đổi weights_1
và bias
để giảm lỗi. Bạn đã thấy rằng bạn có thể sử dụng các dẫn xuất cho việc này, nhưng thay vì một hàm chỉ có tổng bên trong, bây giờ bạn có một hàm tạo ra kết quả của nó bằng cách sử dụng các hàm khác.
Vì bây giờ bạn có thành phần hàm này, nên để lấy đạo hàm của lỗi liên quan đến các tham số, bạn sẽ cần sử dụng quy tắc chuỗi từ phép tính. Với quy tắc chuỗi, bạn sẽ lấy các đạo hàm riêng của mỗi hàm, đánh giá chúng và nhân tất cả các đạo hàm riêng để có được đạo hàm bạn muốn.
Bây giờ bạn có thể bắt đầu cập nhật trọng số. Bạn muốn biết cách thay đổi trọng số để giảm lỗi. Điều này ngụ ý rằng bạn cần phải tính đạo hàm của lỗi đối với trọng số. Vì lỗi được tính bằng cách kết hợp các hàm khác nhau, nên bạn cần lấy các đạo hàm riêng của các hàm này.
Dưới đây là phần trình bày trực quan về cách bạn áp dụng quy tắc chuỗi để tìm đạo hàm của lỗi liên quan đến trọng số:
Biểu đồ cho thấy các đạo hàm riêng bên trong mạng nơron
Mũi tên đậm màu đỏ hiển thị dẫn xuất bạn muốn, đó là derror_dweights
. Bạn sẽ bắt đầu từ hình lục giác màu đỏ, thực hiện đường nghịch đảo của việc đưa ra dự đoán và tính toán các đạo hàm riêng ở mỗi hàm.
Trong hình trên, mỗi hàm được biểu diễn bằng các hình lục giác màu vàng và các đạo hàm riêng được biểu diễn bằng các mũi tên màu xám ở bên trái. Áp dụng quy tắc chuỗi, giá trị của derror_dweights
sẽ như sau:
derror_dweights = (
derror_dprediction * dprediction_dlayer1 * dlayer1_dweights
)
Để tính đạo hàm, bạn nhân tất cả các đạo hàm riêng theo đường dẫn từ hình lục giác sai số (hình màu đỏ) đến hình lục giác mà bạn tìm thấy trọng số (hình lục giác ngoài cùng bên trái).
Bạn có thể nói rằng đạo hàm của y = f(x)
là đạo hàm của f
đối với x
. Sử dụng danh pháp này cho derror_dprediction
, bạn muốn biết đạo hàm của hàm tính toán sai số đối với giá trị dự đoán.
Đường đi ngược lại này được gọi là truyền ngược. Trong mỗi lần truyền ngược, bạn tính các đạo hàm riêng của mỗi hàm, thay thế các biến bằng các giá trị của chúng, và cuối cùng là nhân mọi thứ.
Phần “lấy đạo hàm riêng, đánh giá và nhân” này là cách bạn áp dụng quy tắc chuỗi. Thuật toán này để cập nhật các tham số mạng nơron được gọi là lan truyền ngược (backpropagation).
Điều chỉnh các tham số với lan truyền ngược
Trong phần này, bạn sẽ từng bước thực hiện quy trình lan truyền ngược, bắt đầu với cách bạn cập nhật độ lệch. Bạn muốn lấy đạo hàm của hàm lỗi đối với độ lệch derror_dbias
. Khi đó bạn sẽ tiếp tục lùi, lấy các đạo hàm riêng cho đến khi bạn tìm thấy biến bias
.
Vì bạn đang bắt đầu từ cuối và đi lùi, nên trước tiên bạn cần lấy đạo hàm riêng của sai số đối với dự đoán. Đó là derror_dprediction
trong hình ảnh dưới đây:
Một biểu đồ cho thấy các đạo hàm riêng để tính toán gradient thiên vị
Hàm tạo ra lỗi là một hàm bình phương và đạo hàm của hàm này là 2 * x
, như bạn đã thấy trước đó. Bạn đã áp dụng đạo hàm riêng đầu tiên ( derror_dprediction
) và vẫn không đạt được độ chệch, vì vậy bạn cần lùi lại một bước nữa và lấy đạo hàm của dự đoán đối với lớp trước là dprediction_dlayer1
.
Dự đoán là kết quả của hàm sigmoid. Bạn có thể lấy đạo hàm của hàm sigmoid bằng cách nhân sigmoid(x)
với 1 - sigmoid(x)
. Công thức đạo hàm này rất tiện dụng vì bạn có thể sử dụng kết quả sigmoid đã được tính toán để tính đạo hàm của nó. Sau đó, bạn lấy đạo hàm riêng này và tiếp tục đi lùi.
Bây giờ bạn sẽ lấy đạo hàm layer_1
đối với sự thiên vị. Đây rồi - cuối cùng thì bạn cũng đã làm được! Biến bias
là một biến độc lập, vì vậy kết quả sau khi áp dụng các quy tắc lũy thừa là 1
. Tuyệt vời, bây giờ bạn đã hoàn thành bước lùi này, bạn có thể kết hợp mọi thứ lại với nhau và tính toán derror_dbias
:
In [36]: def sigmoid_deriv(x):
...: return sigmoid(x) * (1-sigmoid(x))
In [37]: derror_dprediction = 2 * (prediction - target)
In [38]: layer_1 = np.dot(input_vector, weights_1) + bias
In [39]: dprediction_dlayer1 = sigmoid_deriv(layer_1)
In [40]: dlayer1_dbias = 1
In [41]: derror_dbias = (
...: derror_dprediction * dprediction_dlayer1 * dlayer1_dbias
...: )
Để cập nhật trọng số, bạn thực hiện theo quy trình tương tự, quay ngược lại và lấy đạo hàm riêng cho đến khi bạn đến biến trọng số. Vì bạn đã tính toán một số đạo hàm riêng, nên bạn chỉ cần tính toán dlayer1_dweights
. Đạo hàm của tích chấm là đạo hàm của vectơ thứ nhất nhân với vectơ thứ hai, cộng với đạo hàm của vectơ thứ hai nhân với vectơ thứ nhất.
Tạo lớp mạng nơ-ron
Bây giờ bạn biết cách viết các biểu thức để cập nhật cả trọng số và độ lệch. Đã đến lúc tạo một lớp (class) cho mạng nơ-ron. Các lớp là khối xây dựng chính của lập trình hướng đối tượng (OOP). Lớp NeuralNetwork
tạo ra các giá trị bắt đầu ngẫu nhiên cho các trọng số và các biến thiên vị.
Khi khởi tạo một đối tượng NeuralNetwork
, bạn cần truyền tham số learning_rate
. Bạn sẽ sử dụng predict()
để đưa ra dự đoán. Các phương thức _compute_derivatives()
và _update_parameters()
để thực hiện những tính toán mà bạn đã học ở trên. Đây là lớp NeuralNetwork
cuối cùng:
class NeuralNetwork:
def __init__(self, learning_rate):
self.weights = np.array([np.random.randn(), np.random.randn()])
self.bias = np.random.randn()
self.learning_rate = learning_rate
def _sigmoid(self, x):
return 1 / (1 + np.exp(-x))
def _sigmoid_deriv(self, x):
return self._sigmoid(x) * (1 - self._sigmoid(x))
def predict(self, input_vector):
layer_1 = np.dot(input_vector, self.weights) + self.bias
layer_2 = self._sigmoid(layer_1)
prediction = layer_2
return prediction
def _compute_gradients(self, input_vector, target):
layer_1 = np.dot(input_vector, self.weights) + self.bias
layer_2 = self._sigmoid(layer_1)
prediction = layer_2
derror_dprediction = 2 * (prediction - target)
dprediction_dlayer1 = self._sigmoid_deriv(layer_1)
dlayer1_dbias = 1
dlayer1_dweights = (0 * self.weights) + (1 * input_vector)
derror_dbias = (
derror_dprediction * dprediction_dlayer1 * dlayer1_dbias
)
derror_dweights = (
derror_dprediction * dprediction_dlayer1 * dlayer1_dweights
)
return derror_dbias, derror_dweights
def _update_parameters(self, derror_dbias, derror_dweights):
self.bias = self.bias - (derror_dbias * self.learning_rate)
self.weights = self.weights - (
derror_dweights * self.learning_rate
)
Đến đây là xong: Bạn đã có code của mạng nơ-ron đầu tiên của bạn. Xin chúc mừng! Code này chỉ đơn giản là tập hợp tất cả các phần bạn đã thấy trước đó ở trên. Nếu bạn muốn đưa ra dự đoán, trước tiên bạn tạo một đối tượng của NeuralNetwork()
, sau đó bạn gọi .predict()
:
In [42]: learning_rate = 0.1
In [43]: neural_network = NeuralNetwork(learning_rate)
In [44]: neural_network.predict(input_vector)
Out[44]: array([0.79412963])
Đoạn mã trên sẽ đưa ra dự đoán, nhưng bây giờ bạn cần học cách đào tạo mạng. Mục đích là làm cho mạng được tổng quát hóa trên tập dữ liệu đào tạo. Điều này có nghĩa là bạn muốn nó thích ứng với dữ liệu mới, chưa nhìn thấy tuân theo cùng một phân phối xác suất như tập dữ liệu đào tạo. Đó là những gì bạn sẽ làm trong phần tiếp theo.
Đào tạo mạng với nhiều dữ liệu hơn
Bạn đã điều chỉnh trọng số và độ lệch cho một trường hợp dữ liệu, nhưng mục tiêu là làm cho mạng tổng quát hóa trên toàn bộ tập dữ liệu. Stochastic gradient descent là một kỹ thuật, trong đó tại mỗi lần lặp lại, mô hình sẽ đưa ra dự đoán dựa trên một phần dữ liệu huấn luyện được chọn ngẫu nhiên, tính toán lỗi và cập nhật các tham số.
Bây giờ đã đến lúc tạo phương thức train()
của lớp NeuralNetwork
của bạn. Bạn sẽ lưu lỗi trên tất cả các điểm dữ liệu cứ sau 100 lần lặp lại vì bạn muốn vẽ biểu đồ cho thấy chỉ số này thay đổi như thế nào khi số lần lặp tăng lên. Dưới đây là phương thức train()
cuối cùng của mạng nơ-ron của bạn:
1class NeuralNetwork:
2 # ...
3
4 def train(self, input_vectors, targets, iterations):
5 cumulative_errors = []
6 for current_iteration in range(iterations):
7 # Pick a data instance at random
8 random_data_index = np.random.randint(len(input_vectors))
9
10 input_vector = input_vectors[random_data_index]
11 target = targets[random_data_index]
12
13 # Compute the gradients and update the weights
14 derror_dbias, derror_dweights = self._compute_gradients(
15 input_vector, target
16 )
17
18 self._update_parameters(derror_dbias, derror_dweights)
19
20 # Measure the cumulative error for all the instances
21 if current_iteration % 100 == 0:
22 cumulative_error = 0
23 # Loop through all the instances to measure the error
24 for data_instance_index in range(len(input_vectors)):
25 data_point = input_vectors[data_instance_index]
26 target = targets[data_instance_index]
27
28 prediction = self.predict(data_point)
29 error = np.square(prediction - target)
30
31 cumulative_error = cumulative_error + error
32 cumulative_errors.append(cumulative_error)
33
34 return cumulative_errors
Có rất nhiều điều đang diễn ra trong khối mã trên, dưới đây là những phân tích cho từng dòng:
- Dòng 8 chọn một phiên bản ngẫu nhiên từ tập dữ liệu.
- Các dòng từ 14 đến 16 tính các đạo hàm riêng và trả về các đạo hàm cho độ lệch và trọng số. Chúng sử dụng
_compute_gradients()
, đây là phương thức mà ta đã xác định trước đó. - Dòng 18 cập nhật độ chệch và trọng số sử dụng phương thức
_update_parameters()
mà bạn đã định nghĩa trong khối mã trước đó. - Dòng 21 kiểm tra xem chỉ số lặp hiện tại có phải là bội số của
100
không. Bạn làm điều này để quan sát lỗi thay đổi như thế nào sau mỗi100
lần lặp lại. - Dòng 24 bắt đầu vòng lặp đi qua tất cả các đối tượng dữ liệu.
- Dòng 28 tính kết quả
prediction
. - Dòng 29 tính toán
error
cho từng đối tượng. - Dòng 31 là nơi bạn tích lũy tổng các lỗi bằng cách sử dụng biến
cumulative_error
. Bạn làm điều này vì bạn muốn vẽ một điểm có lỗi cho tất cả các trường hợp dữ liệu. Sau đó, trên dòng 32, bạn nốierror
vàocumulative_errors
, mảng lưu trữ lỗi. Bạn sẽ sử dụng mảng này để vẽ biểu đồ.
Nói tóm lại, bạn chọn một phiên bản ngẫu nhiên từ tập dữ liệu, tính toán độ dốc và cập nhật trọng số và độ lệch. Bạn cũng tính toán lỗi tích lũy cứ sau 100 lần lặp và lưu các kết quả đó trong một mảng. Bạn sẽ vẽ mảng này để hình dung lỗi thay đổi như thế nào trong quá trình đào tạo.
Lưu ý: Nếu bạn đang chạy code trong Jupyter Notebook, thì bạn cần khởi động lại nhân (kernel) sau khi thêm train() vào lớp NeuralNetwork.
Để giữ cho mọi thứ ít phức tạp hơn, bạn sẽ sử dụng một tập dữ liệu chỉ với tám trường hợp, là mảng input_vectors
. Bây giờ bạn có thể gọi train()
và sử dụng Matplotlib để vẽ biểu đồ lỗi tích lũy cho mỗi lần lặp:
In [45]: # Paste the NeuralNetwork class code here
...: # (and don't forget to add the train method to the class)
In [46]: import matplotlib.pyplot as plt
In [47]: input_vectors = np.array(
...: [
...: [3, 1.5],
...: [2, 1],
...: [4, 1.5],
...: [3, 4],
...: [3.5, 0.5],
...: [2, 0.5],
...: [5.5, 1],
...: [1, 1],
...: ]
...: )
In [48]: targets = np.array([0, 1, 0, 1, 0, 1, 1, 0])
In [49]: learning_rate = 0.1
In [50]: neural_network = NeuralNetwork(learning_rate)
In [51]: training_error = neural_network.train(input_vectors, targets, 10000)
In [52]: plt.plot(training_error)
In [53]: plt.xlabel("Iterations")
In [54]: plt.ylabel("Error for all training instances")
In [54]: plt.savefig("cumulative_error.png")
Bạn khởi tạo lại lớp NeuralNetwork
và gọi train()
bằng cách sử dụng các giá trị input_vectors
và target
. Bạn chỉ định rằng nó sẽ chạy 10000
lần. Đây là biểu đồ hiển thị lỗi cho một trường hợp của mạng nơ-ron:
Biểu đồ hiển thị lỗi đào tạo tích lũy
Lỗi tổng thể đang giảm, đó là những gì bạn muốn. Hình ảnh được tạo trong cùng một thư mục mà bạn đang chạy IPython. Sau lần giảm lớn nhất, lỗi tiếp tục tăng và giảm nhanh chóng từ tương tác này sang tương tác khác. Đó là bởi vì tập dữ liệu là ngẫu nhiên và rất nhỏ, vì vậy mạng nơ-ron khó có thể trích xuất bất kỳ tính năng nào.
Nhưng không nên đánh giá hiệu suất bằng số liệu này vì bạn đang đánh giá nó bằng cách sử dụng các phiên bản dữ liệu mà mạng đã thấy. Điều này có thể dẫn đến việc trang bị quá mức, khi mô hình phù hợp với tập dữ liệu đào tạo đến mức nó không tổng quát hóa thành dữ liệu mới.
Thêm nhiều lớp (layer) hơn vào mạng nơ-ron
Tập dữ liệu trong hướng dẫn này được giữ ở mức nhỏ cho mục đích học tập. Thông thường, các mô hình học sâu cần một lượng lớn dữ liệu vì các bộ dữ liệu thường phức tạp hơn và có rất nhiều sắc thái.
Vì những bộ dữ liệu này có nhiều thông tin phức tạp hơn, nên chỉ sử dụng một hoặc hai lớp là không đủ. Đó là lý do tại sao mô hình học sâu được gọi là "sâu". Chúng thường có một số lượng lớn các lớp.
Bằng cách thêm nhiều lớp hơn và sử dụng các chức năng kích hoạt, bạn sẽ tăng sức mạnh biểu đạt của mạng và có thể đưa ra các dự đoán cấp rất cao. Ví dụ về các loại dự đoán này là nhận dạng khuôn mặt, chẳng hạn như khi bạn chụp ảnh khuôn mặt của mình bằng điện thoại và điện thoại sẽ mở khóa nếu nhận dạng được hình ảnh đó là bạn.
Phần kết luận
Xin chúc mừng bạn! Đến đây thì bạn đã xây dựng được một mạng nơ-ron từ đầu bằng NumPy. Với kiến thức này, bạn đã sẵn sàng đi sâu hơn vào thế giới trí tuệ nhân tạo trong Python.
Trong hướng dẫn này, bạn đã học được:
- Học sâu là gì và phân biệt với khái niệm học máy
- Cách biểu diễn vectơ với NumPy
- Các chức năng kích hoạt là gì và tại sao chúng được sử dụng trong mạng nơ-ron
- Thuật toán lan truyền ngược là gì và cách thức hoạt động
- Cách đào tạo mạng nơ-ron và đưa ra dự đoán
Quá trình đào tạo một mạng nơ-ron chủ yếu bao gồm việc áp dụng các phép toán đối với vectơ. Hôm nay, bạn đã làm điều đó từ đầu chỉ sử dụng NumPy làm phụ thuộc. Điều này không được khuyến nghị trong cài đặt thực tế (production) vì toàn bộ quy trình có thể không hiệu quả và dễ xảy ra lỗi. Đó là một trong những lý do tại sao các framework học sâu như Keras, PyTorch và TensorFlow lại rất phổ biến.
Đọc thêm
Để biết thêm thông tin về các chủ đề được đề cập trong hướng dẫn này, hãy xem các tài nguyên sau: