Hướng dẫn trực quan, dễ hiểu dành cho người mới.
PyTorch là gì?
PyTorch hiện là một trong những framework deep learning phổ biến nhất. Đây là một thư viện mã nguồn mở được xây dựng dựa trên Torch Library (một dự án hiện không còn được phát triển tích cực), và được phát triển bởi Meta AI (trước đây là Facebook AI). Hiện nay PyTorch là một phần của Linux Foundation.
Tensor cơ bản
Machine Learning (ML) về cơ bản là làm việc với các con số. Tensor là một dạng container chuyên dụng để lưu trữ các con số đó. Bạn có thể đã nghe đến tensor trong toán học hoặc vật lý, nhưng trong machine learning, tensor đơn giản chỉ là kiểu dữ liệu của PyTorch dùng để lưu số. Hãy tưởng tượng nó giống như một phiên bản mạnh hơn của list hoặc array. Tensor chứa dữ liệu huấn luyện và các trọng số mà mô hình học được.
Điểm đặc biệt của tensor là nó đi kèm rất nhiều hàm hữu ích. Khi tạo tensor mới, bạn cần gán giá trị ban đầu cho nó. PyTorch cung cấp nhiều hàm khởi tạo như: torch.rand(), torch.randn(), torch.ones()...
Sự khác biệt giữa chúng là gì? Nếu chúng tạo số ngẫu nhiên thì là loại ngẫu nhiên nào? Và tại sao có nhiều cách khởi tạo tensor như vậy?
Cách tốt nhất để hiểu là quan sát trực tiếp.

import torch
rand_sample = torch.rand(10000)
randn_sample = torch.randn(10000)
zeros_sample = torch.zeros(10)
ones_sample = torch.ones(10)
arange_sample = torch.arange(0, 10)
linspace_sample = torch.linspace(0, 10, steps=5)
eye_sample = torch.eye(5)
empty_sample = torch.empty(10)
Khi xem dưới dạng histogram, bạn sẽ thấy rõ sự khác biệt:
torch.rand()tạo số ngẫu nhiên từ 0 đến 1torch.randn()tạo số ngẫu nhiên phân bố quanh 0torch.eye()tạo ma trận đơn vịtorch.zeros()tạo tensor toàn số 0torch.empty()chỉ cấp phát bộ nhớ nhưng không khởi tạo giá trị
Nếu bạn thấy giá trị 0 trong torch.empty() thì đó chỉ là trùng hợp. Bạn nên ghi dữ liệu vào tensor trước khi đọc từ nó.
Sử dụng dữ liệu của riêng bạn
Việc khởi tạo tensor bằng số ngẫu nhiên hữu ích trong một số trường hợp, nhưng cuối cùng bạn vẫn muốn huấn luyện mô hình bằng dữ liệu thực.
| Phòng ngủ | Diện tích (m²) | Tuổi nhà (năm) | Giá (£k) |
|---|---|---|---|
| 2 | 65 | 15 | 285 |
| 3 | 95 | 8 | 425 |
| 4 | 120 | 25 | 380 |
| 3 | 88 | 42 | 295 |
| 5 | 180 | 3 | 675 |
| 2 | 58 | 50 | 245 |
# mỗi dòng là một căn nhà
houses = torch.tensor([
[2,65,15,285],
[3,95,8,425],
[4,120,25,380],
[3,88,42,295],
[5,180,3,675],
[2,58,50,245]
], dtype=torch.float32)
Chuyển đổi dữ liệu sang dạng số
Không phải dữ liệu nào cũng là số. Ví dụ: văn bản, hình ảnh hoặc mô hình 3D. Vì vậy chúng ta cần chuyển đổi chúng thành số trước khi đưa vào mô hình.
Văn bản
"hello" → 0
"world" → 1
Hình ảnh
Ảnh chỉ là một lưới pixel chứa giá trị màu RGB từ 0–255. Ví dụ:
- Ảnh grayscale 28×28 → tensor [28,28]
- Ảnh màu → tensor [3,28,28]
Xem thêm ví dụ tại Hello ML.
Mesh 3D
Mô hình 3D được xác định bởi các vertex. Mỗi vertex có tọa độ x, y, z.
Một mô hình có 1000 vertex sẽ là tensor dạng [1000,3].
Phép toán tensor
PyTorch cung cấp hơn 100 phép toán tensor. Xem chi tiết trong tài liệu chính thức.
Phép toán cơ bản
import torch
x = torch.tensor([1.0,2.0,3.0])
y = torch.tensor([4.0,5.0,6.0])
print(x + y)
print(x * y)
print(x @ y)
print(x.sum())
print(x.mean())
print(x.max())
Activation functions
import torch
import torch.nn.functional as F x = torch.tensor([-2,-1,0,1,2])
print(F.relu(x))
print(torch.sigmoid(x))
print(torch.tanh(x))
Autograd và đạo hàm tự động
Autograd là engine tính đạo hàm tự động của PyTorch. Nó giúp tính gradient cho neural network mà không cần viết công thức đạo hàm thủ công.

Gradient descent
Gradient descent là thuật toán giúp mô hình tìm điểm có loss thấp nhất. Một optimizer phổ biến là Adam.
Quy trình training:
- Tính gradient
- Cập nhật trọng số
- Lặp lại nhiều lần
Xây dựng neural network đơn giản
Để minh họa, chúng ta xây dựng một mô hình dự đoán giá nhà.
Nếu dữ liệu dạng bảng, bạn nên thử XGBoost hoặc LightGBM trước khi dùng neural network.
Import thư viện
import torch
import torch.nn as nn
import torch.nn.functional as F
import pandas as pd from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
from sklearn.metrics import mean_absolute_error, mean_absolute_percentage_error
import matplotlib.pyplot as plt
Chuẩn bị dữ liệu

Dataset: london_houses_transformed.csv
Các bước chuẩn bị dữ liệu:
- Tách feature và target
- Chia train/test
- Chuẩn hóa dữ liệu
- Chuyển sang tensor
Định nghĩa model
class Model(nn.Module):
def __init__(self, in_features=87, h1=64, h2=32, output_features=1):
super().__init__()
self.fc1 = nn.Linear(in_features, h1)
self.fc2 = nn.Linear(h1, h2)
self.out = nn.Linear(h2, output_features)
def forward(self, x):
x = F.relu(self.fc1(x))
x = F.relu(self.fc2(x))
x = self.out(x)
return x
model = Model()
Training Loop
Đã đến lúc đưa mô hình của chúng ta “đi học”! Training loop là nơi mô hình thực sự học từ dữ liệu.
Mỗi lần chạy qua toàn bộ dataset được gọi là một epoch. Trong mỗi epoch sẽ diễn ra các bước sau:
Forward Pass
Đưa dữ liệu huấn luyện vào mô hình để tạo ra dự đoán:
model.forward(X_train)
Calculate Loss
Chúng ta đo mức độ sai lệch của dự đoán bằng hàm MSELoss().
Backpropagationloss.backward() tính toán xem mỗi trọng số đóng góp bao nhiêu vào sai số.
Update Weightsoptimiser.step() điều chỉnh các trọng số để giảm sai số.
Chúng ta sử dụng Adam optimizer, một thuật toán tự động điều chỉnh learning rate cho từng tham số.
Clear Gradientsoptimiser.zero_grad() đặt lại gradient để chúng không bị cộng dồn sang epoch tiếp theo.
Quy trình này được lặp lại 100 lần với learning rate = 0.01.
Sau mỗi epoch, giá trị loss sẽ giảm dần khi mô hình học cách dự đoán giá nhà chính xác hơn.
epochs = 100
learning_rate = 0.01
torch.manual_seed(15)
# EACH TRAINING LOOP, WE'LL STORE THE LOSS
# THIS IS OPTIONAL, USED FOR VISUALISING LOSS GRAPH
losses = []
optimiser = torch.optim.Adam(model.parameters(), learning_rate)
loss_func = nn.MSELoss()
for i in range(epochs):
optimiser.zero_grad()
y_pred = model.forward(X_train)
# MEASURE THE LOSS/ERROR
loss = loss_func(y_pred, Y_train)
# (OPTIONAL) ADDING LOSS FROM THE CURRENT EPOCH
losses.append(loss.detach().numpy())
# (OPTIONAL) PRINTING LOSS EVERY 500th EPOCH
if i % 500 == 0:
print(f'Epoch: {i} loss: {loss}')
# CLEAR GRADIENTS FROM PREVIOUS STEP
optimiser.zero_grad()
# BACKPROPAGATION
loss.backward()
# UPDATE WEIGHTS
optimiser.step()
# SAVE OUR FINAL MODEL
torch.save(model.state_dict(), 'model.pth')
(Tùy chọn) trực quan hóa hàm loss
Chúng ta có thể vẽ biểu đồ để xem loss giảm dần theo thời gian huấn luyện.
plt.figure(figsize=(10, 6))
plt.plot(losses, linewidth=2, color='#e74c3c')
plt.xlabel('EPOCH', fontsize=12)
plt.ylabel('MSE Loss (normalised)', fontsize=12)
plt.title('Training Progress: Loss Approaching Zero', fontsize=14, fontweight='bold')
plt.grid(True, alpha=0.3)
plt.ylim(bottom=0)
plt.tight_layout()
plt.show()
Kiểm tra mô hình
Sau khi huấn luyện xong, chúng ta kiểm tra mô hình bằng tập dữ liệu test (dữ liệu mà mô hình chưa từng thấy).
Chúng ta so sánh dự đoán của mô hình với giá thực tế, sau đó tính hai chỉ số:
-
MAE (Mean Absolute Error) – sai số tuyệt đối trung bình
-
MAPE (Mean Absolute Percentage Error) – sai số phần trăm trung bình
# TELLS PYTORCH WE ARE IN INFERENCE MODE
model.eval()
with torch.no_grad():
predictions = model(X_test)
# DENORMALISE BACK TO REAL PRICES:
predictions_real = predictions * price_std + price_mean
Y_test_real = Y_test * price_std + price_mean
print("\nTEST PREDICTIONS (UNSEEN DATA):")
mae = mean_absolute_error(Y_test_real, predictions_real)
mape = mean_absolute_percentage_error(Y_test_real, predictions_real) * 100
# CALCULATE PERCENTAGE ERRORS
pct_errors = torch.abs((Y_test_real - predictions_real) / Y_test_real) * 100
within_10 = (pct_errors <= 10).sum().item()
within_20 = (pct_errors <= 20).sum().item()
total = len(Y_test_real)
print(f"\nOverall performance:")
print(f" MAE: £{mae:,.0f}")
print(f" MAPE: {mape:.1f}%")
print(f" Within 10%: {within_10}/{total} ({within_10/total*100:.0f}%)")
print(f" Within 20%: {within_20}/{total} ({within_20/total*100:.0f}%)")
Kết quả
Overall performance:
MAE: £329,798
MAPE: 18.6%
Within 10%: 257/689 (37%)
Within 20%: 447/689 (65%)
Kết luận
Chúng ta vừa xây dựng một pipeline Machine Learning hoàn chỉnh từ đầu:
-
Chuẩn bị dữ liệu
-
Huấn luyện mô hình
-
Backpropagation
-
Đánh giá kết quả
Kết quả cho thấy thách thức thực sự không nằm ở mô hình mà nằm ở feature.
Giá bất động sản phụ thuộc rất nhiều vào vị trí, nhưng các feature hiện tại của chúng ta chưa thể mô tả chi tiết yếu tố này.
Đây là một thực tế phổ biến trong Machine Learning:
Một mô hình tốt không thể bù đắp cho dữ liệu thiếu thông tin quan trọng.
Lần sau, hãy bắt đầu với feature tốt hơn, hoặc cân nhắc sử dụng
XGBoost khi làm việc với tabular data.