Neural Network
Giả sử bạn muốn dự đoán điểm thi từ số giờ ôn tập. Ở bài Linear Regression, ta dùng một đường thẳng: cứ học thêm một giờ thì điểm dự đoán tăng thêm một lượng cố định.
Nhưng dữ liệu thực tế có thể không đi theo một đường thẳng như vậy. Lúc mới bắt đầu, học thêm một giờ có thể giúp tiến bộ nhiều; khi đã gần đạt điểm tối đa, học thêm một giờ có thể chỉ cải thiện rất ít. Hai học sinh học cùng số giờ cũng có thể đạt kết quả khác nhau vì số bài luyện tập và kiến thức nền khác nhau.
Neural Network giúp ta học những mối quan hệ phức tạp hơn. Thay vì đi thẳng từ input đến dự đoán bằng một phép tính, mạng cho dữ liệu đi qua nhiều bước tính toán. Mỗi bước kết hợp các thông tin đã có, rồi đưa kết quả sang bước tiếp theo.
Trong bài này, ta đi từ một khối tính toán nhỏ nhất, ghép các khối thành mạng, rồi xem mạng học cách dự đoán ra sao.
Định nghĩa: Neural Network là gì?
Neural Network, hay mạng nơ-ron nhân tạo, là một mô hình ghép nhiều khối tính toán nhỏ với nhau. Mỗi khối nhận vào các con số và trả ra một con số. Kết quả của khối này có thể trở thành đầu vào của khối tiếp theo.
Mỗi khối đó được gọi là neuron. Các neuron thường được xếp thành từng layer, tức là từng tầng. Khi học từ dữ liệu, mạng điều chỉnh các con số bên trong neuron để dự đoán tốt hơn.
Neuron: Một khối tính toán: nhận các input, kết hợp chúng theo một công thức rồi tạo ra một output. Đây là phép tính trong máy, không phải tế bào thần kinh thật.
Layer (tầng): Một nhóm neuron nằm ở cùng một bước xử lý. Các neuron trong tầng đó nhận dữ liệu từ tầng trước.
Mạng cơ bản nhất: một neuron
Tiếp tục bài toán điểm thi. Ta cung cấp cho mô hình hai thông tin: số giờ học và số bài luyện tập. Một neuron nhận cả hai thông tin này để tính ra một kết quả.
Vuốt ngang để xem trọn sơ đồ →
Hãy đọc hình từ trái sang phải. Một neuron làm hai việc:
- Nhân rồi cộng: nhân từng input với một trọng số riêng, cộng các kết quả và thêm bias. Gọi tổng vừa tính là .
- Xử lý tổng vừa có: đưa qua một hàm gọi là activation, rồi nhận output .
Feature (đặc trưng): Một thông tin đầu vào. Trong ví dụ này, giờ học là một feature, số bài luyện tập là một feature khác.
Weight (trọng số): Mỗi input được nhân với một weight riêng trước khi cộng vào tổng . Weight quyết định input đó làm thay đổi như thế nào: nếu , input tăng thì tăng ; nếu , input tăng thì giảm , khi các input khác và tham số giữ nguyên.
Bias: Con số được cộng vào sau khi đã nhân từng input với weight và cộng các kết quả lại. Bias giúp tăng hoặc giảm tổng : nếu giữ nguyên inputs và weights rồi tăng từ lên , tổng tăng thêm ; nếu giảm đi , tổng cũng giảm . Khi tất cả input bằng , ta có .
Activation function: Hàm nhận tổng và biến nó thành output của neuron. Chúng ta sẽ nói về activation function sau.
Viết hai bước ấy bằng công thức:
Ở đây, là hai input; là hai weights; là bias. Ký hiệu chỉ hàm activation ta chọn. là kết quả trước activation, còn là kết quả sau activation.
Vậy có ý nghĩa gì? chính là output mà neuron tạo ra bằng cách kết hợp các features đầu vào rồi áp dụng activation. Trong ví dụ điểm thi, neuron nhận số giờ học và số bài luyện tập, nhân chúng với các weights tương ứng, cộng bias rồi đưa tổng qua hàm . Kết quả là một con số mới: .
Output của neuron (): Giá trị neuron trả về sau khi xử lý các input. Giá trị này có thể được dùng để đưa ra dự đoán, hoặc tiếp tục làm input cho neuron khác.
Điểm quan trọng là không nhất thiết là kết quả cuối cùng của cả mạng. Với neuron vừa tính, là output; nhưng khi ta đưa con số ấy sang một neuron tiếp theo, nó lại đóng vai trò input. Neuron tiếp theo có thể kết hợp với output của các neuron khác để tạo ra một kết quả mới. Nối tiếp các bước như vậy, ta có mạng nhiều layer.
Khi có nhiều layer
Bây giờ, ta áp dụng ý tưởng dùng output làm input cho tầng tiếp theo. Thay vì chỉ có một neuron, ta cho ba neuron cùng nhìn vào hai input. Mỗi neuron có weights và bias riêng, nên tạo ra một output riêng.
Ta nhận được ba con số mới từ hai features ban đầu. Cả ba con số trở thành inputs cho nhóm neuron ở tầng tiếp theo. Tầng này lại kết hợp các inputs để tạo output mới, rồi truyền tiếp cho đến tầng tạo dự đoán cuối cùng:
Vuốt ngang để xem trọn sơ đồ →
Theo dõi một lượt đi qua hình:
- Input layer: đưa hai thông tin ban đầu vào mạng.
- Hidden layer 1: ba neuron tính riêng rẽ và tạo ra ba kết quả.
- Hidden layer 2: mỗi neuron nhận cả ba kết quả vừa có, kết hợp chúng để tạo ra kết quả mới. Tầng này có hai neuron nên trả ra hai con số.
- Output layer: nhận hai con số đó và tạo một dự đoán cuối cùng.
Input layer (tầng đầu vào): Nơi đưa dữ liệu vào mạng. Các nút ở đây chỉ biểu diễn features, chưa làm phép tính của neuron.
Hidden layer (tầng ẩn): Tầng nằm giữa input và output.
Output layer (tầng đầu ra): Tầng tạo kết quả ta cần, chẳng hạn điểm thi dự đoán.
Mũi tên trong hình cho biết neuron nhận dữ liệu từ đâu. Ở mạng này, mỗi neuron nhận tất cả kết quả của tầng ngay trước nó. Kiểu kết nối đó gọi là fully connected.
Fully connected (kết nối đầy đủ): Mỗi neuron nối với tất cả output của tầng liền trước. Mỗi kết nối có một weight; mỗi neuron có một bias riêng.
Hình minh họa có hai hidden layer, nhưng mạng có thể chỉ có một hoặc có nhiều hơn. Phần tính tay phía sau sẽ dùng mạng nhỏ hơn: 2 input → 3 hidden neuron → 1 output, để bạn dễ theo dõi từng con số.
Activation function
Có nhiều neuron và nhiều layer chưa đủ để giải quyết vấn đề ở đầu bài. Nếu mọi neuron chỉ nhân rồi cộng, cả mạng vẫn có thể thu gọn về một mô hình tuyến tính.
Xem ví dụ đơn giản với một input. Tầng thứ nhất tính ra , tầng thứ hai dùng để dự đoán:
Ta đã đi qua hai tầng, nhưng kết quả cuối vẫn là một đường thẳng theo . Thêm tầng chỉ thay đổi hệ số, chưa tạo được quan hệ phức tạp hơn.
Activation phi tuyến làm cho mạng không còn bị giới hạn như vậy. Nó thay đổi cách xử lý tùy giá trị nhận được. Chẳng hạn, hàm ReLU dưới đây xử lý số âm khác với số dương.
ReLU
ReLU là activation function phổ biến nhất, và nó có một quy tắc rất dễ nhớ: số âm đổi thành 0; số dương giữ nguyên; số 0 vẫn là 0.
ReLU: Tên một activation function. Công thức có nghĩa là chọn số lớn hơn giữa 0 và .
Thử lần lượt ba giá trị:
Đường trên hình nằm ngang ở phần âm và đi lên ở phần dương: nó có một chỗ gãy, thay vì là một đường thẳng xuyên suốt. Khi nhiều neuron dùng ReLU với các weights khác nhau, mạng có thể ghép nhiều đoạn đơn giản để mô tả quan hệ phức tạp hơn.
Chọn activation theo vai trò: Hidden layer thường dùng ReLU. Ở output, regression có thể giữ nguyên tổng để dự đoán một con số; classification nhị phân dùng sigmoid để chuyển tổng thành xác suất.
Forward propagation
Ta đã biết mạng được nối như thế nào. Bây giờ hãy đưa một bộ input vào và tính từ trái sang phải: tầng đầu tính xong thì chuyển kết quả cho tầng sau, cho đến khi có dự đoán. Toàn bộ lượt tính đó gọi là forward propagation.
Forward propagation: Một lượt tính từ input qua các layer để ra dự đoán.
Ta dùng mạng 2 → 3 → 1: hai input, một hidden layer có ba neuron dùng ReLU và một output neuron. Bộ weights dưới đây được chọn để tính tay dễ, chưa phải tham số đã train.
Đi từng bước qua Neural Network
2 input → 3 hidden neuron → 1 output. Bấm Next để theo dõi phép tính; weights và bias giữ cố định.
Nhập đủ hai số từ −10 đến 10 để tiếp tục.
Vuốt ngang sơ đồ để xem các layer →
x₁ = 2, x₂ = 1. Các neuron chưa được tính.
Xem weights và bias cố định
h₁: w = (1, 1), b = −1
h₂: w = (−1, 1), b = 0
h₃: w = (0.5, 1), b = 0
Output: v = (0.5, −1, 1), b = −1
Đây là tham số chọn để minh họa phép tính, chưa được học từ dữ liệu. Các số hiển thị được làm tròn đến 4 chữ số thập phân.
Tự tính lại ví dụ mặc định
Bước 1 — Đưa input vào: , . Lúc này chưa có dự đoán; ta mới chỉ cung cấp hai con số cho mạng.
Bước 2 — Tính tổng tại từng hidden neuron. Mỗi neuron có bộ weights và bias riêng:
Bước 3 — Áp dụng ReLU:
Hidden layer trả ra ba con số . Ta đặt tên chúng là để dễ theo dõi. Neuron ở output sẽ dùng ba con số này, thay vì dùng trực tiếp hai input ban đầu.
Bước 4 — Tính tổng ở output. Gọi weights ở đây là để dễ phân biệt với weights ở hidden layer:
Bước 5 — Chuyển thành dự đoán phù hợp với bài toán:
| Bài toán | Xử lý output | Ví dụ |
|---|---|---|
| Regression | Giữ nguyên tổng: | , một giá trị liên tục. |
| Binary classification | Đưa tổng qua sigmoid: | ; threshold 0.5 cho nhãn 1. |
Hidden features có ý nghĩa gì?
Hãy tưởng tượng là giờ học, là số bài luyện tập, và output dùng để dự đoán kết quả thi. Sau khi mạng học từ nhiều ví dụ, ta có thể hình dung ý nghĩa của từng output ở hidden layer như sau:
- có thể phản ánh “mức độ chuẩn bị”: giá trị cao khi học sinh vừa dành nhiều thời gian học, vừa làm nhiều bài luyện tập.
- có thể phản ánh “thiên về luyện tập”: giá trị cao khi học sinh làm nhiều bài so với thời gian học.
- có thể phản ánh “học nhiều nhưng ít luyện tập”: giá trị cao khi học sinh dành nhiều giờ học nhưng làm ít bài.
Đây là những ví dụ để hình dung cách mạng kết hợp hai thông tin ban đầu thành các thông tin mới, không phải ý nghĩa đã được xác nhận của ba neuron trong công cụ phía trên.
Bạn nhập vào hai thông tin: giờ học và số bài. Mạng tự tính ra ba thông tin mới là . Những thông tin mới này gọi là hidden features. Tầng sau dùng chúng như “nguyên liệu” để tính tiếp.
Hidden feature: Đặc trưng trung gian do mạng tính ra; giá trị của nó biết được, nhưng ý nghĩa đối với con người không có nhãn cố định.
Nhưng ta không đưa cho mạng yêu cầu “neuron thứ nhất phải đo mức độ chuẩn bị”. Ta chỉ yêu cầu nó dự đoán kết quả cuối cho tốt. Vì vậy, những tên trên chỉ là phỏng đoán về ý nghĩa. Ta tính được chính xác giá trị của hidden feature, nhưng không thể chỉ nhìn vào con số đó rồi khẳng định nó mang ý nghĩa gì.
Khả năng tự học những feature mới như vậy là một điểm khác biệt quan trọng giữa deep learning và nhiều cách làm machine learning truyền thống. Với Linear Regression và Logistic Regression trong hai bài trước, ta đưa các features đã chọn vào công thức để dự đoán. Nếu muốn bổ sung thông tin như “số bài luyện tập trên mỗi giờ học”, ta thường phải tự nghĩ ra cách kết hợp này rồi tính nó thành một feature mới cho mô hình.
Với deep learning, ta vẫn cung cấp dữ liệu đầu vào, nhưng mạng có thể tự học cách kết hợp và biến đổi các features qua nhiều tầng. Tầng đầu tạo các hidden features từ dữ liệu ban đầu; tầng sau lại dùng chúng làm input để tạo những features phức tạp hơn. Trong lúc huấn luyện, weights và biases được điều chỉnh để những cách kết hợp này phục vụ việc dự đoán. Ta không cần định nghĩa sẵn ý nghĩa cho từng hidden feature.
Quay lại ví dụ điểm thi: từ giờ học và số bài luyện tập, mạng tạo ra , rồi có thể tiếp tục kết hợp chúng ở tầng sau trước khi dự đoán điểm. Mạng vừa học cách tạo features, vừa học cách dùng các features đó để dự đoán. Những features học được có thể hữu ích hơn việc chỉ dùng trực tiếp thông tin ban đầu, nhưng điều này vẫn cần được kiểm tra trên dữ liệu mới.
Deep learning và machine learning: Deep learning là một nhánh của machine learning, không phải hai lĩnh vực tách biệt. Điểm nổi bật của deep learning là dùng nhiều tầng để tự học các cách biểu diễn dữ liệu; nhiều phương pháp machine learning truyền thống phụ thuộc nhiều hơn vào các features do con người lựa chọn hoặc thiết kế.
Hàm mất mát (Loss function)
Sau khi mạng đưa ra dự đoán, ta cần so sánh với đáp án thật để biết mô hình sai bao nhiêu. Neural Network có thể dùng lại hai hàm loss đã gặp: MSE cho dự đoán giá trị liên tục và BCE cho phân loại nhị phân.
Loss function: Cách đo dự đoán sai bao nhiêu bằng một con số để hướng dẫn việc học.
MSE cho regression
Giống bài Linear Regression, khi dự đoán điểm thi, ta lấy dự đoán trừ điểm thật, bình phương sai số rồi tính trung bình:
MSE: Trung bình bình phương sai số, thường dùng khi dự đoán giá trị liên tục.
Ở đây, là số mẫu; là giá trị thật và là dự đoán của mẫu thứ .
Ví dụ hai dự đoán là , đáp án thật là :
Bình phương giúp lỗi âm và dương không triệt tiêu nhau, đồng thời phạt lỗi lớn mạnh hơn. MSE càng nhỏ thì dự đoán càng gần đáp án thật trên các mẫu đang xét.
BCE cho binary classification
Giống bài Logistic Regression, khi dự đoán đậu hay trượt, ta dùng Binary Cross-Entropy để so sánh xác suất dự đoán với nhãn thật:
BCE: Loss cho nhãn 0/1, phạt mạnh khi xác suất dự đoán nghiêng về lớp sai.
Ở đây, là số mẫu; là nhãn thật (0 hoặc 1), là xác suất dự đoán lớp 1 và là logarit tự nhiên. Với một mẫu, công thức rút gọn thành:
- Nếu : loss là . Xác suất lớp 1 càng cao thì loss càng nhỏ.
- Nếu : loss là . Xác suất lớp 1 càng thấp thì loss càng nhỏ.
Ví dụ, học sinh thực sự đậu () và mạng dự đoán xác suất đậu :
Nếu mạng chỉ dự đoán , loss tăng lên . Dự đoán càng tự tin vào lớp sai, BCE phạt càng nặng. Ta tính BCE từ xác suất, chưa đổi xác suất thành nhãn bằng threshold.
Backpropagation
Ở hai bài trước, bạn đã biết cách dùng Gradient Descent để giảm loss: tính gradient của loss theo từng tham số, rồi cập nhật tham số theo hướng ngược gradient. Neural Network vẫn học theo ý tưởng đó.
Chẳng hạn, để cập nhật một weight , ta vẫn dùng công thức quen thuộc:
Trong đó, là learning rate, còn cho biết loss thay đổi thế nào khi thay đổi một chút. Quy tắc cập nhật không đổi. Điều cần giải quyết là tính gradient cho tất cả weights và biases trong một mạng nhiều tầng.
Mạng có nhiều tầng nên việc tính gradient không còn gọn như ở hai mô hình trước. Với backpropagation, ta tính lần lượt: bắt đầu từ loss để tính gradient ở tầng cuối, rồi dùng kết quả đó tính ngược về tầng ngay trước nó. Cứ tiếp tục như vậy, ta tính được gradient cho các weights và biases ở những tầng đầu mạng.
Nói đơn giản, gradient ở tầng trước cần thông tin đã tính từ tầng sau. Chính vì đi từ cuối mạng ngược về đầu mạng nên cách tính này được gọi là backpropagation — lan truyền ngược.
Backpropagation (lan truyền ngược): Cách tính gradient của loss theo các weights và biases bằng cách đi ngược qua các phép tính của mạng. Các kết quả gradient ở bước sau được dùng lại để tính gradient ở bước trước.
Forward: input → hidden → output → lossBackward: gradient từ loss → output → hidden → các weights đầu vàoBackprop dựa trên chain rule: nếu weight làm hidden output thay đổi, và hidden output làm dự đoán thay đổi, ta ghép các mức ảnh hưởng ấy để biết weight làm loss thay đổi thế nào.
Chain rule (quy tắc dây chuyền): Quy tắc tính đạo hàm qua nhiều phép tính nối tiếp.
Backpropagation cung cấp gradient; Gradient Descent dùng gradient đó để cập nhật tham số. Hai bước phối hợp với nhau: chạy backward mới chỉ tính được thông tin cần để sửa weights, chưa thay đổi weights.
Tính tay với chính mạng vừa xem
Ta dùng chế độ regression, với input , và đáp án thật . Trước khi tính ngược, hãy nhắc lại kết quả forward:
| Thành phần | Giá trị đang dùng |
|---|---|
| Tổng ở ba hidden neuron | |
| Output sau ReLU | |
| Weights từ hidden đến output | |
| Bias của output |
Dự đoán và loss của một mẫu là:
Ta sẽ tính gradient cho ở output, rồi đi ngược về , weight nối input với hidden neuron thứ nhất. Giá trị hiện tại của là . Trong lúc tính gradient, ta giữ nguyên tất cả tham số, chưa cập nhật gì cả.
Sơ đồ dưới đây tách riêng đường đi qua neuron thứ nhất. Hãy đọc hàng trên từ trái sang phải để theo dõi forward, rồi đọc hàng dưới từ phải sang trái để theo dõi gradient. Các bước sẽ được giải thích ngay sau hình.
Forward → Đọc hàng trên từ trái sang phải để tính loss.
← Backward Đọc hàng dưới từ phải sang trái: bắt đầu từ loss, rồi lần lượt tính gradient theo dự đoán, hidden output, tổng và weight.
Bước 1: Tính gradient từ loss về dự đoán
Tạm chưa nghĩ đến weights. Ta chỉ hỏi: nếu dự đoán tăng một chút, loss thay đổi thế nào?
Ta có . Đạo hàm của biểu thức bình phương bằng hai lần biểu thức đó, nhân với đạo hàm bên trong. Đạo hàm của theo bằng , nên:
Cách đọc đạo hàm: nghĩa là tại dự đoán hiện tại, nếu tăng một lượng rất nhỏ thì loss giảm xấp xỉ hai lần lượng đó. Đây là mức thay đổi cục bộ, không có nghĩa tăng dự đoán bao nhiêu cũng làm loss giảm.
Thử tăng dự đoán từ lên :
Loss giảm từ xuống , tức giảm , gần với . Dấu âm phù hợp với trực giác: dự đoán đang thấp hơn đáp án, tăng lên một chút sẽ giảm sai số.
Bước 2: Tính gradient cho weight ở output
Xét , weight nhân với . Giữ nguyên các giá trị khác, ta viết lại công thức dự đoán:
Nếu tăng , dự đoán tăng . Đạo hàm của dự đoán theo chính là hệ số :
Ta đã biết hai mắt xích: làm dự đoán thay đổi, rồi dự đoán làm loss thay đổi. Nhân hai đạo hàm để nối chúng:
Đó chính là chain rule. Kết quả cho biết tăng một chút sẽ làm loss giảm ở bộ tham số hiện tại.
Các weights còn lại ở output được tính cùng cách: lấy gradient nhân với input tương ứng. Với bias, đạo hàm bằng vì bias được cộng trực tiếp vào dự đoán:
Đây là phần được dùng lại: ta tính một lần, rồi dùng nó cho cả ba weights và bias của output.
Bước 3: Tính ngược về output của hidden neuron
Để đi đến , ta cần đi qua trước. Giữ nguyên weights và các nhánh khác:
Nếu tăng một chút, dự đoán tăng bằng một nửa lượng đó. Vì vậy:
Dùng lại gradient từ loss về dự đoán:
Ta đã biết loss thay đổi thế nào khi output của hidden neuron thứ nhất thay đổi. Để tới weight đầu vào, ta còn phải đi ngược qua ReLU và phép nhân với input.
Bước 4: Đi ngược qua ReLU
Ta có và . Với số dương, ReLU giữ nguyên giá trị: thì ; thì . Hai giá trị thay đổi cùng một lượng, nên:
Nhân với gradient ở bước 3:
ReLU ở nhánh này giữ nguyên gradient vì tổng đang dương.
Bước 5: Tính gradient cho weight đầu vào
Cuối cùng, ta tới phép tính chứa :
Vì , tăng một chút làm tăng gấp đôi lượng đó. Do vậy:
Dùng gradient ở bước 4:
Gom lại, ta đã lần lượt tính:
| Tính gradient theo | Dùng kết quả bước trước | Kết quả |
|---|---|---|
| Dự đoán | Đạo hàm của loss | |
| Hidden output | ||
| Tổng | ||
| Weight |
Mỗi bước lấy gradient vừa tính ở phía sau, nhân với đạo hàm của phép tính hiện tại. Đó là cách backprop đi từ loss về một weight ở đầu mạng.
Nếu gặp nhánh ReLU âm thì sao?
Neuron thứ hai có , nên . Nếu tăng nhẹ lên , ReLU vẫn trả về . Thay đổi nhỏ ở chưa làm output thay đổi, nên đạo hàm tại đây bằng .
Weight nối nhánh này với output là . Ta tính ngược:
Từ đi tiếp về weights và bias của neuron thứ hai, gradient đều bằng vì nhân với . Các tham số đó không nhận cập nhật từ mẫu đang xét. Mẫu khác làm vẫn có thể cho gradient khác . Tại đúng , ReLU không có đạo hàm duy nhất; code bên dưới chọn .
Từ gradient đến cập nhật
Bây giờ ta mới dùng Gradient Descent: lấy tham số cũ trừ learning rate nhân với gradient.
là weight hoặc bias đang cập nhật. Chọn learning rate để thử một bước nhỏ.
Learning rate: Hệ số điều chỉnh độ lớn của bước cập nhật. Với , ta nhân gradient với rồi trừ kết quả khỏi tham số hiện tại.
Với weight output , gradient là :
Trừ một số âm tức là cộng, nên weight tăng . Điều này hợp lý vì gradient âm cho biết tăng weight một chút sẽ giúp giảm loss.
Với weight đầu vào , gradient là :
Phải tính xong các gradient bằng cùng bộ tham số cũ rồi mới cập nhật. Khi tính ngược về ở bước 3, ta dùng của lượt forward, chưa được thay bằng .
Để kiểm tra, giả sử ta chỉ cập nhật hai weights vừa tính, giữ nguyên mọi tham số khác, rồi chạy forward lại:
Dự đoán đã gần đáp án hơn, và loss giảm từ xuống khoảng . Ta chỉ cập nhật hai weights ở đây để dễ kiểm tra bằng tay; khi train thực tế, optimizer cập nhật tất cả tham số được huấn luyện bằng gradient tương ứng.
Nhớ hai nhiệm vụ: Backpropagation tính gradient; optimizer dùng gradient để cập nhật tham số. Bước cập nhật quá lớn vẫn có thể làm loss tăng, nên cần chọn learning rate phù hợp.
Training loop: ghép mọi thứ lại
Hãy ghép lại như cách làm bài rồi sửa bài: mạng đưa ra dự đoán, so với đáp án thật, tính xem các tham số ảnh hưởng đến lỗi ra sao, rồi sửa tham số một chút. Làm lại với dữ liệu nhiều lần chính là training loop.
Training loop: Vòng lặp dự đoán → đo loss → tính gradient → cập nhật tham số, lặp lại để mô hình học.
Initialize weights
for each epoch: Forward propagation Calculate loss Backpropagation Calculate gradients Update weightsỞ đây, Calculate gradients là kết quả của Backpropagation, không phải một lượt đạo hàm riêng sau backward. Bản trên coi mỗi epoch dùng cả tập train. Khi chia dữ liệu thành mini-batch, vòng lặp thực tế là:
Initialize weights và bias
for each epoch: Shuffle training data for each mini-batch: Clear old gradients Forward propagation Calculate loss Backpropagation → calculate gradients Optimizer updates weights và bias Evaluate on validation data (không update)Các thuật ngữ cần biết
| Thuật ngữ | Hiểu đơn giản |
|---|---|
| Epoch | Một lượt đi qua toàn bộ tập train. |
| Batch | Nhóm mẫu được xử lý cùng nhau; trong code, “batch” thường chỉ một mini-batch. |
| Full batch | Dùng toàn bộ tập train cho một lần tính gradient và update. |
| Mini-batch | Dùng một phần tập train cho mỗi lần update. |
| Batch size | Số mẫu trong một batch, ví dụ 32. Batch cuối có thể nhỏ hơn. |
| Iteration | Một lượt xử lý batch; trong loop này tương ứng một lần update. |
| Learning rate | Điều khiển độ lớn bước cập nhật; quá lớn dễ bất ổn, quá nhỏ học chậm. |
| Optimizer | Thuật toán quyết định cách dùng gradient để cập nhật tham số. |
Epoch: Một lượt đi qua toàn bộ tập train.
Batch / mini-batch / batch size: Batch là nhóm mẫu xử lý cùng nhau; mini-batch dùng một phần dữ liệu; batch size là số mẫu trong nhóm.
Iteration: Một lượt xử lý batch; trong ví dụ này có một lần cập nhật tham số.
Optimizer: Thuật toán dùng gradient để quyết định cách cập nhật weights và bias.
Ví dụ có 1.000 mẫu, batch size 32, giữ batch cuối: một epoch có iterations. Trong đó 31 batch có 32 mẫu và batch cuối có 8 mẫu. Train 10 epoch sẽ có 320 lần update. Không nhầm 10 epoch với 10 lần cập nhật.
Gradient Descent → SGD → Momentum → Adam
Gradient Descent đi ngược hướng gradient. Nếu tính gradient trên toàn bộ tập train, mỗi bước có thể tốn nhiều bộ nhớ và thời gian.
Gradient Descent: Điều chỉnh tham số ngược hướng gradient để giảm loss từng bước.
Stochastic Gradient Descent (SGD) theo nghĩa chặt dùng một mẫu mỗi bước; thực tế người ta thường gọi cả cách dùng mini-batch là SGD. Gradient của batch chỉ là ước lượng gradient toàn tập, nên loss từng bước có thể dao động. Đổi lại, ta cập nhật được thường xuyên và xử lý dữ liệu lớn hơn.
SGD: Ước lượng gradient từ một mẫu hoặc mini-batch để cập nhật, thay vì tính trên cả tập train mỗi bước.
Momentum tích lũy một phần hướng di chuyển từ các bước trước. Hãy tưởng tượng đẩy một quả bóng xuống dốc: nó có quán tính, thay vì đổi hướng hoàn toàn theo từng gợn nhỏ. Momentum giúp giảm dao động và tiến nhanh hơn theo hướng nhất quán.
Momentum: Giữ một phần hướng cập nhật từ các bước trước, giống quán tính giúp chuyển động bớt đổi hướng liên tục.
Adam cũng nhớ các bước trước, nhưng còn theo dõi độ lớn gradient để điều chỉnh bước đi riêng cho từng tham số. Bạn có thể hiểu rằng các weights không nhất thiết cần được sửa với cùng một mức độ. Bên trong, Adam dùng các giá trị trung bình của gradient và bình phương gradient. Khi mới học, chỉ cần nắm vai trò đó để đọc được phần code; Adam không đảm bảo tốt nhất cho mọi bài toán.
Adam: Optimizer dùng lịch sử gradient và bình phương gradient để điều chỉnh bước đi cho từng tham số.
Trong thực tế, ta hiếm khi tự viết phép cập nhật cho từng weight. Mạng có thể có hàng triệu tham số; thư viện giúp tính gradient và cập nhật chúng theo cùng quy tắc, giảm việc viết code lặp lại và sai sót. Với PyTorch, loss.backward() tính gradient và optimizer.step() cập nhật tham số; xem tài liệu optimizer chính thức. Phần NumPy phía sau sẽ tự viết các bước này để bạn thấy thư viện đang làm gì.
Các vấn đề khi train Neural Network
Overfitting và underfitting
Underfitting giống như chưa nắm được cách giải bài: ngay những ví dụ đã dùng để học, mạng vẫn dự đoán kém. Có thể mô hình quá đơn giản, input thiếu thông tin cần thiết hoặc quá trình học chưa hiệu quả. Ta cần xem cả kết quả trên tập train và trên dữ liệu giữ riêng.
Underfitting: Mô hình chưa học đủ: dự đoán trên chính tập train cũng chưa tốt.
Overfitting giống như học quá sát bộ bài đã luyện: làm tốt trên dữ liệu train nhưng gặp dữ liệu mới lại kém. Mạng có thể đã khớp cả những chi tiết ngẫu nhiên trong tập train. Một dấu hiệu thường gặp là training loss tiếp tục giảm trong khi validation loss có xu hướng tăng qua nhiều epoch.
Overfitting: Mô hình khớp tốt với dữ liệu đã học nhưng dự đoán kém trên dữ liệu mới.
Training loss / validation loss: Training loss đo trên tập học; validation loss đo trên tập giữ riêng để theo dõi và chọn cấu hình.
Đồ thị trên là minh họa xu hướng, không phải kết quả một lần train cụ thể. Một epoch validation loss tăng chưa đủ để kết luận. Khi so sánh, cần dùng cùng cách tính loss; dropout và các chế độ train/eval có thể làm hai đường khác nhau.
| Giải pháp | Vì sao có ích? |
|---|---|
| More data | Thêm dữ liệu đại diện giúp mô hình bớt phụ thuộc vào chi tiết của một tập nhỏ. |
| Data augmentation | Tạo biến thể vẫn giữ đúng nhãn, như dịch nhẹ ảnh. Không áp biến đổi tùy tiện làm đổi ý nghĩa hoặc nhãn. |
| Dropout | Khi train, ngẫu nhiên đưa một phần activation về 0 để mạng bớt phụ thuộc vào một vài neuron. Khi eval, không thả ngẫu nhiên nữa. |
| Weight decay | Khuyến khích weights nhỏ hơn, hạn chế độ phức tạp. Cách thực hiện phụ thuộc optimizer; AdamW tách weight decay khỏi bước thích nghi của Adam. |
| Early stopping | Dừng khi validation không cải thiện đủ lâu và khôi phục checkpoint có validation loss tốt nhất. |
Data augmentation: Tạo biến thể dữ liệu vẫn giữ đúng nhãn, như dịch nhẹ một ảnh.
Dropout: Tạm đưa ngẫu nhiên một phần activation về 0 khi train để giảm phụ thuộc vào vài neuron.
Weight decay: Cơ chế khuyến khích weights nhỏ hơn để hạn chế độ phức tạp của mô hình.
Early stopping / checkpoint: Dừng khi validation không cải thiện đủ lâu; checkpoint là bản tham số đã lưu để có thể khôi phục.
Với underfitting, không nên mặc định tăng dropout. Hãy kiểm tra dữ liệu, khả năng biểu diễn, thời gian train, learning rate và mức regularization. Validation dùng để chọn cấu hình; test để đánh giá cuối cùng, không dùng test để quyết định lúc dừng.
Vanishing gradient và exploding gradient
Trong backprop, gradient đi qua nhiều phép nhân. Nếu các hệ số thường nhỏ, tích có thể co lại: . Layer đầu nhận tín hiệu rất yếu và học chậm — đó là vanishing gradient.
Vanishing gradient: Gradient nhỏ dần khi truyền ngược, khiến một số layer học rất chậm.
Ngược lại, nếu tích tăng mạnh như , gradient có thể trở nên rất lớn — exploding gradient. Dấu hiệu có thể là loss nhảy mạnh, tham số tăng vọt hoặc xuất hiện NaN. Đây là trực giác đơn giản; gradient thực tế còn có ma trận và tổng qua nhiều đường.
Exploding gradient: Gradient tăng quá lớn, có thể làm cập nhật mất ổn định hoặc xuất hiện giá trị không hợp lệ.
- ReLU: ở phía dương, đạo hàm bằng 1 nên tránh một nguồn co gradient do activation bão hòa. Nhưng phía âm có gradient bằng 0; neuron luôn âm trên dữ liệu có thể bị “chết”. ReLU không giải quyết mọi vấn đề gradient.
- Proper initialization: chọn thang weights phù hợp để tín hiệu không bị phóng đại hoặc thu nhỏ quá mức ngay từ đầu.
- Normalization: điều chỉnh thang các activation theo một nhóm giá trị, thường giúp tối ưu ổn định hơn.
- Residual connections: thêm đường tắt khi kích thước phù hợp. Gradient có thêm đường đi trực tiếp qua , thay vì chỉ qua mọi phép biến đổi trong .
- Gradient clipping: giới hạn norm của gradient trước update để giảm tác động của bước quá lớn; thường hữu ích với exploding gradient.
Residual connection: Đường tắt cộng input vào kết quả của một nhóm layer, giúp thông tin và gradient có thêm đường đi trực tiếp.
Gradient clipping: Giới hạn độ lớn gradient trước khi cập nhật để giảm tác động của bước quá mạnh.
Weight initialization
Trước lượt forward đầu tiên, weights phải có giá trị ban đầu. Nếu ba neuron cùng bắt đầu với bộ weights và bias giống nhau, chúng tính ra cùng kết quả và có thể tiếp tục được cập nhật giống nhau. Ta có ba neuron nhưng chúng lại làm cùng một việc.
Vì vậy, ta thường chọn weights ngẫu nhiên để các neuron bắt đầu khác nhau. Đó là ý nghĩa của “phá đối xứng” trong initialization. Bias có thể bắt đầu bằng 0; điều cần tránh là cho mọi neuron cùng một bộ weights và bias như nhau.
Weight initialization: Cách chọn weights ban đầu trước khi train; khởi tạo ngẫu nhiên giúp các neuron không bắt đầu giống hệt nhau.
Nhưng random không có nghĩa là chọn độ lớn tùy ý:
| Cách khởi tạo | Trực giác và cách dùng |
|---|---|
| Random với thang tùy ý | Phá đối xứng, nhưng quá nhỏ hoặc quá lớn có thể làm tín hiệu/gradient yếu hoặc bất ổn. |
| Xavier / Glorot | Cân nhắc cả số kết nối vào và ra, thường phù hợp với tanh hoặc các phép biến đổi gần tuyến tính. |
| He / Kaiming | Cân nhắc việc ReLU loại phần âm; thường dùng cho layer đi kèm ReLU. |
Xavier / He initialization: Hai cách chọn thang weights ban đầu dựa trên số kết nối; Xavier thường hợp với tanh, He thường dùng cùng ReLU.
Thang khởi tạo thường dùng ⚠️ Có toán
Gọi fan_in là số input vào một neuron và fan_out là số output của layer. Một dạng Xavier normal cơ bản dùng variance ; He normal với ReLU dùng variance . Standard deviation là căn bậc hai của variance.
Ví dụ hidden layer có hai input: He normal lấy weights từ phân phối chuẩn có độ lệch chuẩn . Đây là quy tắc giữ thang tín hiệu dưới các giả định nhất định, không đảm bảo mọi mạng đều train tốt. Các hàm thư viện còn có lựa chọn gain, distribution và mode; xem tài liệu khởi tạo PyTorch.
Normalization: BatchNorm và LayerNorm
Hãy tưởng tượng một nhóm giá trị trong mạng có số chỉ quanh 0.1, trong khi một nhóm khác thường lên đến hàng trăm. Sự khác biệt về độ lớn có thể khiến việc cập nhật khó ổn định. Normalization điều chỉnh các giá trị về một thang đo dễ xử lý hơn.
Bạn đã gặp việc chuẩn hóa input trước khi train. BatchNorm và LayerNorm đưa ý tưởng chuẩn hóa vào bên trong mạng, áp dụng cho các kết quả trung gian. Sau bước chuẩn hóa, chúng thường còn có hệ số nhân và phần cộng thêm để mạng học cách điều chỉnh lại nếu cần.
Normalization: Điều chỉnh thang của các giá trị bằng thống kê như trung bình và độ lệch chuẩn.
Cách tính cơ bản là lấy mỗi giá trị trừ đi trung bình của nhóm, rồi chia cho thước đo độ phân tán của nhóm đó. Một số rất nhỏ được thêm vào phép tính để tránh chia cho 0. Khi mới đọc, hãy tập trung vào câu hỏi: chọn những giá trị nào vào cùng một nhóm để chuẩn hóa?
| BatchNorm trong mạng fully connected | LayerNorm trên vector hidden | |
|---|---|---|
| Nhóm tính thống kê | Cùng một feature qua các mẫu trong mini-batch. | Các features được chọn trong từng mẫu riêng biệt. |
| Khi train | Thường dùng thống kê batch và cập nhật trung bình chạy. | Tính thống kê trên từng mẫu. |
| Khi eval | Mặc định dùng thống kê chạy đã tích lũy. | Vẫn tính thống kê trên từng mẫu, không dùng trung bình chạy từ batch. |
| Điều cần chú ý | Batch quá nhỏ có thể cho thống kê kém ổn định. | Không phụ thuộc các mẫu khác trong batch theo cách của BatchNorm. |
BatchNorm: Trong mạng dạng bảng, chuẩn hóa từng feature theo thống kê qua các mẫu trong batch khi train.
LayerNorm: Chuẩn hóa các features được chọn trong từng mẫu riêng biệt, không cần lấy thống kê từ các mẫu khác.
Ví dụ, đưa 32 học sinh qua một hidden layer có 3 neuron. Kết quả là một bảng 32 hàng × 3 cột: mỗi hàng là một học sinh, mỗi cột là kết quả của một neuron. BatchNorm xét từng cột qua 32 học sinh; LayerNorm trong ví dụ này xét 3 giá trị trên từng hàng. Cách hình dung theo cột và hàng giúp phân biệt hai loại mà chưa cần nhớ API.
Normalization không thay thế việc chia dữ liệu đúng và không tự chữa overfitting. Mạng nhỏ trong phần code dưới đây chưa cần thêm BatchNorm hay LayerNorm để bạn tập trung vào forward/backward. Đối chiếu hành vi mặc định tại BatchNorm1d và LayerNorm.
Code Neural Network với PyTorch
Ta tạo dữ liệu hai features có quan hệ phi tuyến, rồi train mạng 2 → 3 → 1 để phân loại nhị phân. Đây là dữ liệu giả lập, không cần tải dataset. Cài các thư viện bằng pip install torch numpy scikit-learn nếu môi trường chưa có.
import copyimport numpy as npimport torchfrom torch import nnfrom torch.utils.data import DataLoader, TensorDatasetfrom sklearn.model_selection import train_test_splitfrom sklearn.preprocessing import StandardScalerfrom sklearn.metrics import accuracy_score, f1_score, roc_auc_score
# 1. Dữ liệu giả lập: hai feature, nhãn 0/1torch.manual_seed(42)rng = np.random.default_rng(42)X = rng.normal(size=(1000, 2)).astype(np.float32)score = X[:, 0] + 0.8 * np.maximum(X[:, 1], 0) - 0.4y = (score + rng.normal(0, 0.25, len(X)) > 0).astype(np.float32)
# 2. Train/validation/test = 60/20/20; giữ tỉ lệ lớpX_train, X_other, y_train, y_other = train_test_split( X, y, test_size=0.4, random_state=42, stratify=y)X_val, X_test, y_val, y_test = train_test_split( X_other, y_other, test_size=0.5, random_state=42, stratify=y_other)scaler = StandardScaler().fit(X_train) # chỉ fit trên train!
def tensors(features, labels): x = torch.tensor(scaler.transform(features), dtype=torch.float32) y = torch.tensor(labels, dtype=torch.float32).reshape(-1, 1) return x, y
xtr, ytr = tensors(X_train, y_train)xv, yv = tensors(X_val, y_val)xt, yt = tensors(X_test, y_test)loader = DataLoader(TensorDataset(xtr, ytr), batch_size=32, shuffle=True)
# 3. Logits ở output: KHÔNG thêm Sigmoid trước BCEWithLogitsLossmodel = nn.Sequential(nn.Linear(2, 3), nn.ReLU(), nn.Linear(3, 1))nn.init.kaiming_normal_(model[0].weight, nonlinearity="relu")nn.init.zeros_(model[0].bias)nn.init.xavier_normal_(model[2].weight)nn.init.zeros_(model[2].bias)loss_fn = nn.BCEWithLogitsLoss()optimizer = torch.optim.Adam(model.parameters(), lr=0.01)
# 4. Train; chọn checkpoint bằng validation, không dùng testbest_val = float("inf")best_state = copy.deepcopy(model.state_dict())patience, stale = 25, 0history = []model.eval()with torch.no_grad(): print(f"Initial train loss: {loss_fn(model(xtr), ytr).item():.4f}")
for epoch in range(300): model.train() for xb, yb in loader: optimizer.zero_grad() # xóa gradient từ batch trước logits = model(xb) # forward: (batch_size, 1) loss = loss_fn(logits, yb) # nhãn cũng (batch_size, 1) loss.backward() # backprop tính gradient optimizer.step() # cập nhật weights và bias
model.eval() with torch.no_grad(): train_loss = loss_fn(model(xtr), ytr).item() val_loss = loss_fn(model(xv), yv).item() history.append((train_loss, val_loss)) if epoch % 25 == 0: print(f"Epoch {epoch + 1}: train={train_loss:.4f}, val={val_loss:.4f}") if val_loss < best_val - 1e-4: best_val = val_loss best_state = copy.deepcopy(model.state_dict()) stale = 0 else: stale += 1 if stale >= patience: break
# 5. Khôi phục model tốt nhất rồi mới đánh giá testmodel.load_state_dict(best_state)model.eval()with torch.no_grad(): test_logits = model(xt) test_loss = loss_fn(test_logits, yt).item() p = torch.sigmoid(test_logits).squeeze(1).numpy()pred = (p >= 0.5).astype(int)print(f"Test BCE: {test_loss:.4f}")print(f"Accuracy: {accuracy_score(y_test, pred):.3f}")print(f"F1: {f1_score(y_test, pred, zero_division=0):.3f}")print(f"ROC-AUC: {roc_auc_score(y_test, p):.3f}")Autograd: Cơ chế tự theo dõi phép tính và tính gradient của thư viện.
Tensor / shape: Tensor là mảng số; shape là kích thước theo từng chiều, như (32, 2) nghĩa là 32 mẫu với 2 features.
Các điểm cần đọc kỹ:
BCEWithLogitsLosskết hợp sigmoid và BCE theo cách ổn định số học. Input của loss là logits, không phải xác suất đã qua sigmoid; xem tài liệu loss chính thức.zero_grad()tránh cộng dồn gradient ngoài ý muốn. PyTorch mặc định tích lũy gradient sau các lần backward.model.eval()đổi hành vi các layer như Dropout/BatchNorm;torch.no_grad()tắt ghi nhận phép tính cho autograd. Hai lệnh có mục đích khác nhau. Mạng này chưa có Dropout/BatchNorm nhưng giữ thói quen đúng từ đầu.historylưu train/validation loss tính ở cuối epoch với cùng chế độ eval, có thể dùng để vẽ learning curves.deepcopygiữ bản weights tốt nhất, không giữ tham chiếu tiếp tục thay đổi khi train.- Accuracy và F1 dùng nhãn; ROC-AUC dùng xác suất. Khi dữ liệu lệch lớp, cần xem thêm confusion matrix, precision và recall.
Chuyển sang regression: giữ output cuối không có activation, đổi nhãn thành giá trị liên tục có shape (n, 1) và dùng nn.MSELoss(). Bỏ sigmoid/threshold lúc dự đoán; đánh giá bằng MAE, RMSE hoặc R². Với target liên tục, bỏ stratify=y trong cách split ở trên. Không lấy nhãn 0/1 của ví dụ này rồi gọi đó là bài toán dự đoán điểm thi.
Code Neural Network từ đầu bằng NumPy
Bản này dùng cùng kiến trúc và quy luật tạo dữ liệu, tự tính gradient rồi update bằng full-batch Gradient Descent. Dùng cả tập train mỗi bước giúp ta đọc công thức rõ hơn; bản PyTorch phía trên đã minh họa mini-batch. Hai ví dụ chạy độc lập.
import numpy as np
rng = np.random.default_rng(42)X = rng.normal(size=(1000, 2))score = X[:, 0] + 0.8 * np.maximum(X[:, 1], 0) - 0.4y = (score + rng.normal(0, 0.25, len(X)) > 0).astype(float)[:, None]indices = rng.permutation(len(X))train_ids, test_ids = indices[:800], indices[800:]mean, std = X[train_ids].mean(axis=0), X[train_ids].std(axis=0)std = np.maximum(std, 1e-8)X_train, X_test = (X[train_ids] - mean) / std, (X[test_ids] - mean) / stdy_train, y_test = y[train_ids], y[test_ids]
W1 = rng.normal(size=(2, 3)) * np.sqrt(2 / 2) # He cho ReLUb1 = np.zeros((1, 3))W2 = rng.normal(size=(3, 1)) * np.sqrt(2 / (3 + 1))b2 = np.zeros((1, 1))
def sigmoid(s): # exp luôn nhận số không dương, tránh overflow e = np.exp(-np.abs(s)) return np.where(s >= 0, 1 / (1 + e), e / (1 + e))
def forward(x): z1 = x @ W1 + b1 h = np.maximum(0, z1) s = h @ W2 + b2 return z1, h, s
def bce(s, target): # BCE từ logits: max(s, 0) - y*s + log(1 + exp(-abs(s))) return np.mean(np.maximum(s, 0) - target * s + np.log1p(np.exp(-np.abs(s))))
lr = 0.05for epoch in range(2000): z1, h, s = forward(X_train) loss = bce(s, y_train)
# Backprop; mean loss nên chia số mẫu đúng một lần ds = (sigmoid(s) - y_train) / len(X_train) dW2 = h.T @ ds db2 = ds.sum(axis=0, keepdims=True) dz1 = (ds @ W2.T) * (z1 > 0) dW1 = X_train.T @ dz1 db1 = dz1.sum(axis=0, keepdims=True)
# Tất cả gradient đã được tính bằng weights cũ W1 -= lr * dW1 b1 -= lr * db1 W2 -= lr * dW2 b2 -= lr * db2 if epoch % 400 == 0: print(f"Epoch {epoch}: train BCE={loss:.4f}")
_, _, train_s = forward(X_train)_, _, test_s = forward(X_test)pred = sigmoid(test_s) >= 0.5print(f"Final train BCE: {bce(train_s, y_train):.4f}")print(f"Test BCE: {bce(test_s, y_test):.4f}")print(f"Test accuracy: {(pred == y_test).mean():.3f}")X_train có shape (800, 2), W1 là (2, 3), nên hidden features có shape (800, 3). Nhân tiếp W2 có shape (3, 1) cho logits (800, 1), khớp với shape nhãn. Viết nhãn thành (800,) ở đây có thể gây broadcasting sai và tạo ra một ma trận loss không mong muốn.
Bản NumPy cố định số epoch để minh họa; không dùng test để chỉnh learning rate hay chọn epoch. Khi thử nhiều cấu hình, hãy tách thêm validation như bản PyTorch. Loss thường giảm theo xu hướng với cấu hình phù hợp, không phải lời hứa giảm ở mọi iteration hoặc mọi seed.
Dùng AI để code Neural Network
Bạn có thể nhờ AI viết phần code lặp lại, nhưng cần hiểu forward/backward để kiểm tra kết quả. Trước khi tin một con số đẹp, hãy rà soát:
- Có chia train/validation/test trước khi fit scaler không?
- Output và loss có khớp không: logits +
BCEWithLogitsLoss, hay output tuyến tính + MSE? - Shape và dtype của nhãn có đúng không? Có broadcasting ngoài ý muốn không?
- Có
zero_grad(),backward()vàstep()đúng thứ tự không? - Có dùng
train(),eval()vàno_grad()đúng mục đích không? - Có theo dõi validation và khôi phục checkpoint tốt nhất không? Có dùng test để chọn model không?
- Metric có phù hợp, có so sánh với baseline đơn giản không?
- Có seed, kiểm tra loss hữu hạn và quan sát learning curves không?
Data leakage: Thông tin từ validation/test lọt vào quá trình học, khiến kết quả đánh giá thiếu tin cậy.
Baseline: Cách dự đoán đơn giản dùng làm mốc so sánh cho neural network.
Một prompt mẫu:
Hãy viết code Python dùng PyTorch cho binary classification với hai features.Dùng mạng Linear(2, 3) → ReLU → Linear(3, 1), BCEWithLogitsLoss và Adam.Tạo dữ liệu giả lập phi tuyến, chia train/validation/test trước khi fit scaler.Train theo mini-batch, theo dõi train/validation loss, early stopping và khôi phụccheckpoint tốt nhất. Đánh giá test bằng accuracy, F1, ROC-AUC và confusion matrix.Giải thích shape ở mỗi layer, zero_grad/backward/step, train/eval/no_grad,và vì sao không thêm sigmoid trước BCEWithLogitsLoss.Cuối cùng chỉ ra các lỗi thường gặp và cách kiểm tra gradient bằng finite differences.Mini project
Project 1: Dự đoán điểm từ dữ liệu giả lập. Tạo hai features giờ học và số bài luyện tập, target có quan hệ phi tuyến và nhiễu. Train mạng regression, so sánh RMSE với Linear Regression trên cùng split. Vẽ predicted vs actual; giải thích tại sao hidden features không có sẵn tên gọi chắc chắn.
Project 2: Quan sát overfitting. Dùng bài classification ở trên, giảm số mẫu train và tăng hidden neurons. Vẽ train/validation loss, sau đó thử từng thay đổi: weight decay, dropout, early stopping. Chọn cấu hình bằng validation và chỉ đánh giá test cuối cùng. Đừng thay tất cả cùng lúc vì sẽ khó biết yếu tố nào tạo khác biệt.
Tóm tắt
- Neuron tính tổng có trọng số cộng bias rồi áp dụng activation. Nhiều layer học các biểu diễn trung gian.
- Activation phi tuyến giúp mạng vượt khỏi một phép biến đổi tuyến tính; ReLU giữ phần dương và đưa phần âm về 0.
- Forward tạo dự đoán; loss đo sai; backprop tính gradient; optimizer cập nhật tham số.
- Output và loss phụ thuộc bài toán: output tuyến tính + MSE cho regression; sigmoid + BCE về mặt toán học cho binary classification.
- Train cần theo dõi validation, initialization, gradient và regularization, không chỉ tăng số epoch.
- Thư viện tự làm phép tính, nhưng người viết code vẫn phải kiểm tra dữ liệu, shape, loss và cách đánh giá.
Ôn tập
Làm 4 câu trắc nghiệm, 1 câu điền số và 2 câu tự luận ngắn. Trắc nghiệm và câu điền số được kiểm tra tự động; câu tự luận có đáp án gợi ý để bạn tự đối chiếu.