Tensor và autograd

Từ mảng nhiều chiều tới gradient tự động

Chương 3 · Bài 3.1

2

Mảng này mang nghĩa gì?

Hai mẫu dữ liệu, mỗi mẫu có ba đặc trưng.

Ta muốn tạo hai đặc trưng mới rồi đo lỗi dự đoán.

shape = (2, 3)

  • Chiều 0: mẫu.
  • Chiều 1: đặc trưng.
  • Tensor còn có dtype và device.
3

Mục tiêu

  • Đoán shape của tensor trước khi chạy code.
  • Phân biệt nhân từng phần tử, nhân ma trận, reshape và đổi chiều.
  • Tính gradient bằng tay và kiểm bằng autograd.
  • Dùng detach, no_grad; chuyển tensor giữa CPU và GPU.
01

Shape trước, code sau

Shape trước, code sau5

Chiều nào bị gộp?

X @ W + b: chiều 3 bị gộp, hai mẫu giữ nguyên.123456X (2, 3)@100111W (3, 2)+0.5−0.5b (2,)=4.54.510.510.5Y (2, 2)1·1 + 2·0 + 3·1 + 0.5 = 4.5chiều 3 bị gộp; hai mẫu giữ nguyên
X @ W + b: chiều 3 bị gộp, hai mẫu giữ nguyên.
Shape trước, code sau6

Shape của một lớp linear

Y=XW+b.Y=XW+b.

BB là số mẫu, dd là số feature, hh là số feature đầu ra.

Tensor Shape
X (B,d)
W (d,h)
b (h,)
Y (B,h)
X = torch.tensor([[1., 2., 3.], [4., 5., 6.]])
W = torch.tensor([[1., 0.], [0., 1.], [1., 1.]])
b = torch.tensor([0.5, -0.5])
Y = X @ W + b
print(Y.tolist())
Kết quả[[4.5, 4.5], [10.5, 10.5]]

Mỗi hàng vẫn thuộc một mẫu.

Shape trước, code sau7

dtype đi theo dữ liệu

print(torch.tensor([1, 2, 3]).dtype,
      torch.tensor([1., 2., 3.]).dtype)
Kết quảtorch.int64 torch.float32
  • Trọng số, đặc trưng: float32.
  • Nhãn lớp, token ID: int64 (long).
  • Tensor số nguyên không đặt được requires_grad=True.
Shape trước, code sau8

Hỏi nhanh

X có shape (8, 3), W có shape (3, 5). X @ W có shape gì?

Shape trước, code sau9

Cùng shape, khác phép

A = torch.arange(6).reshape(2, 3); hai hàng ban đầu là [0,1,2] và [3,4,5].

print(A.reshape(3, 2).tolist())
print(A.transpose(0, 1).tolist())
Kết quả[[0, 1], [2, 3], [4, 5]]
[[0, 3], [1, 4], [2, 5]]

Reshape chia lại dãy phần tử.

Transpose đổi hàng và cột.

Shape đúng chưa chứng minh ý nghĩa đúng.

Shape trước, code sau10

Reshape và transpose

Cùng shape (3, 2): reshape giữ thứ tự, transpose đưa cột thành hàng.012345A (2, 3)012345A.reshape(3, 2)031425A.transpose(0, 1)hàng đầu sau reshape: [0, 1]hàng đầu: [0, 3]
Cùng shape (3, 2): reshape giữ thứ tự, transpose đưa cột thành hàng.
Shape trước, code sau11

permute và reshape trên một ảnh thật

Đổi (H, W, 3) sang (3, H, W): permute giữ đúng kênh màu, reshape trộn lẫn.
Shape trước, code sau12

Hỏi nhanh

Bảng A có shape (2, 3). Muốn đổi thành (3, 2) sao cho các cột cũ thành hàng mới, ta dùng gì?

Shape trước, code sau13

Broadcasting âm thầm

Prediction (B,1) trừ target (B,).

Kết quả thành (B,B): so mọi cặp mẫu.

Sửa: pred.squeeze(-1).

Chặn sớm:

assert pred.shape == target.shape

Shape trước, code sau14

(2, 1) trừ (2,)

(2, 1) trừ (2,) thành bảng (2, 2), nên MSE ra 0.5 thay vì 0.12pred (2, 1)−12target (2,)=0−110(2, 2)mean((·)²) = 0.5MSE đúng = 0mỗi dự đoán bị trừ cho mọi nhãn; chỉ đường chéo là cặp đúng
(2, 1) trừ (2,) thành bảng (2, 2), nên MSE ra 0.5 thay vì 0.
Shape trước, code sau15

Hỏi nhanh

Dự đoán pred = [[1.], [2.]], shape (2, 1), khớp đúng nhãn target = [1., 2.], shape (2,). Vậy mà ((pred - target) ** 2).mean() lại in ra 0.5. Vì sao?

02

Gradient đi ngược DAG

Gradient đi ngược DAG17

Một đường tính toán nhỏ

Forward 2 → 6 → 25; backward nhân đạo hàm cục bộ: 10 × 3 = 30.2w6u = 3w25J = (u − 1)²× 3(·−1)²dJ/du = 2(u − 1) = 10du/dw = 330101dJ/dw = 10 × 3 = 30
Forward 2 → 6 → 25; backward nhân đạo hàm cục bộ: 10 × 3 = 30.
Gradient đi ngược DAG18

Đồ thị dựng trong lúc chạy

Forward chạy đến đâu, DAG được ghi đến đó; mỗi forward dựng lại DAG mới.

if và vòng lặp Python vẫn đạo hàm được: backward theo nhánh đã chạy.

Một biến đi qua nhiều nhánh: gradient các nhánh được cộng.

Gradient đi ngược DAG19

Hỏi nhanh

Cho x = torch.tensor(-3., requires_grad=True), rồi y = 2 * x if x > 0 else x ** 2 và y.backward(). x.grad bằng bao nhiêu?

Gradient đi ngược DAG20

Đạo hàm qua hai bước

w=2,u=3w=6,J=(u−1)2=25.w=2,\quad u=3w=6,\quad J=(u-1)^2=25. dJdw=2(u−1)⋅3=30.\frac{dJ}{dw}=2(u-1)\cdot3=30.

Gradient dương: tăng ww một chút thì loss tăng.

Gradient đi ngược DAG21

Autograd đối chiếu

requires_grad=True: ghi lại mọi phép tính dùng w.

w = torch.tensor(2., requires_grad=True)
u = 3 * w
J = (u - 1) ** 2
J.backward()

Kết quả trong notebook:

Kết quả25.0 30.0

Loss 25 · gradient 30.

Backward chưa cập nhật trọng số.

Gradient đi ngược DAG22

Bước cập nhật là việc riêng

w←2−0.01⋅30=1.7.w\leftarrow2-0.01\cdot30=1.7.

with torch.no_grad(): khi cập nhật thủ công.

Optimizer sẽ làm bước ấy trong training loop.

Gradient đi ngược DAG23

Hỏi nhanh

Vẫn J=(3w−1)2J = (3w - 1)^2, nhưng tại w=0w = 0. Gradient bằng bao nhiêu, và một bước gradient descent đẩy ww theo hướng nào?

Gradient đi ngược DAG24

Mỗi lần backward cộng thêm

Mỗi lần backward cộng thêm: 30 rồi 60; xóa grad thì lại 30.v.grad30backward60backward-grad = None30backward30 + 30: cộng dồn, không ghi đè
Mỗi lần backward cộng thêm: 30 rồi 60; xóa grad thì lại 30.
Gradient đi ngược DAG25

Gradient cộng dồn

Hai lần forward/backward độc lập.

Giữa lần hai và ba: v.grad = None.

Kết quả30.0
60.0
30.0

Xóa gradient cũ trước bước mới.

Mỗi bước: forward mới → backward mới.

Gradient đi ngược DAG26

Hỏi nhanh

Một bạn tính loss một lần trước vòng lặp, rồi mỗi epoch gọi loss.backward() và optimizer.step(). Sang epoch thứ hai thì báo RuntimeError: Trying to backward through the graph a second time. Sửa thế nào?

03

Graph và thiết bị

Graph và thiết bị28

Ba thao tác, ba mục đích

  • detach(): cắt đường gradient.
  • clone(): sao dữ liệu.
  • .item(): lấy số Python để ghi log.
  • no_grad(): ngừng ghi graph trong khối.

Tại v=2:

v.square().detach() * v

Forward là 8, gradient là 4.

Nhánh square bị coi là hằng số.

Graph và thiết bị29

Detach cắt một đường

Giữ graph: gradient 12. Detach v²: chỉ còn 4.giữ nguyên graph: v · v²2v² = 4v³ = 8v.grad = 3v² = 12v² đã detach: hằng 4 · v2v² = 4v³ = 8v.grad = 4
Giữ graph: gradient 12. Detach v²: chỉ còn 4.
Graph và thiết bị30

Hỏi nhanh

w bằng 2 và đã có w.grad bằng 30. Chạy snap = w.detach(), rồi with torch.no_grad(): w -= 0.1 * w.grad. snap.item() in ra gì?

Graph và thiết bị31

Cùng device mới tính cùng nhau

device = torch.device(
    'cuda' if torch.cuda.is_available()
    else 'cpu')
X_device = X.to(device)
linear = torch.nn.Linear(3, 2).to(device)

Tensor: .to trả bản mới, phải gán lại.

Module: .to chuyển tại chỗ.

Máy chạy bài không có CUDA:

Kết quảcpu (2, 2)
(2, 2)

Có CUDA thì cùng code chọn cuda.

GPU không tự nhanh hơn với dữ liệu rất nhỏ.

Graph và thiết bị32

Hỏi nhanh

Model đã gọi model.to('cuda'). Trong vòng lặp có dòng X.to('cuda'), không gán lại, rồi model(X), và PyTorch báo các tensor nằm trên hai device khác nhau. Vì sao?

Graph và thiết bị33

Gradient bằng None hay sai số?

Lần theo đường bị cắt:

  • detach(), no_grad()
  • .item(), float() giữa forward
  • tham số không tham gia forward
u = torch.tensor(3 * w.item())
J = (u - 1) ** 2 + w

Đúng ra dJ/dw=31dJ/dw=31; autograd chỉ thấy phần +w+w, cho 1.

Graph và thiết bị34

Khi đi thi

  • assert pred.shape == target.shape.
  • Nhãn lớp long; hồi quy, BCE float.
  • device đặt một chỗ, không gõ cứng .cuda().
04

Luyện đề

Luyện đề36

Luyện đề 1

Một model khai báo ba lớp fc1, fc2, fc3 trong __init__, nhưng forward chỉ gọi fc1 rồi fc2. Sau loss.backward(), fc3.weight.grad là gì?

Phỏng theoNOAI 2026 Singapore · Vòng sơ loại · Câu 54

Luyện đề37

Luyện đề 2

X có shape (64, 10): 64 mẫu, mỗi mẫu 10 đặc trưng. Để trừ mỗi hàng cho trung bình của chính hàng đó, một bạn viết m = X.mean(dim=1) rồi X - m. Kết quả thế nào?

Phỏng theoNOAI 2026 Singapore · Vòng sơ loại · Câu 104

Luyện đề38

Luyện đề 3

loss là tensor 0 chiều, logits là tensor (32, 10), cả hai còn nối với graph. Ta muốn (a) ghi loss vào log dưới dạng số Python, và (b) giữ logits ở dạng tensor nhưng không giữ graph. Gọi gì?

Phỏng theoNOAI 2026 Singapore · Vòng sơ loại · Câu 145

Luyện đề39

Chốt ý

Tensor có số, shape, dtype và device; đọc cả bốn trước khi tính.

Autograd ghi lại phép tính lúc forward; backward cộng gradient theo mọi đường, chưa cập nhật trọng số.

Luyện đề40

Sau giờ học

Làm 10 câu kiểm tra cuối bài trên trang bài học.

Chạy notebook: trước mỗi ô, đoán shape, dtype và gradient sẽ in ra, rồi đối chiếu.