Mở đầu: Từ mảng động JavaScript đến Scripting trong AI
Đối với đa số lập trình viên phát triển web, JavaScript (JS) là ngôn ngữ thống trị tuyệt đối nhờ mô hình vạn vật bất đồng bộ dựa trên Event Loop và khả năng chạy trực tiếp trên trình duyệt. Tuy nhiên, khi bước chân vào lãnh địa của Trí Tuệ Nhân Tạo (AI) và Học Máy (Machine Learning), Python mới là tiêu chuẩn công nghiệp không thể chối cãi. Điều này dễ dẫn đến tâm lý e ngại vì phải làm quen với một cú pháp mới và cách quản lý công cụ hoàn toàn xa lạ.
Đừng lo lắng! Bài học này sẽ giúp bạn bắc một cây cầu tư duy vững chắc từ các khái niệm JS quen thuộc sang cú pháp tương đương của Python. Bạn sẽ thấy rằng việc học Python thực ra rất dễ dàng khi chúng ta đối chiếu trực tiếp các cấu trúc lệnh, cơ chế phân phối gói thư viện, và đặc biệt là cách xử lý vùng nhớ giữa hai ngôn ngữ. Nắm chắc bài học này sẽ giúp bạn tự tin viết các script đầu tiên và cấu hình môi trường sạch sẽ để huấn luyện AI.
python3 --version
Nếu máy in ra một dòng như
Python 3.11.6 thì bạn đã đủ điều kiện, bỏ qua phần còn lại của
khung này. Nếu nó báo command not found thì bạn chưa có Python: macOS:
brew install python3 · Windows: tải bộ cài từ
python.org và nhớ tick "Add Python to PATH" ở bước đầu (bỏ qua ô này là nguyên nhân phổ biến
nhất khiến terminal không thấy Python sau khi cài) · Linux:
sudo apt install python3 python3-venv. Một lưu ý nhỏ nhưng hay gây bối rối: trên macOS và Linux, lệnh là
python3 chứ không phải
python. Trên Windows thường là python. Bài này viết python3; nếu
bạn dùng Windows thì bỏ số 3 đi.
1. Môi trường runtime: Python Interpreter vs Node.js
Điểm tương đồng lớn nhất giữa Python và JavaScript là cả hai đều là ngôn ngữ thông dịch (interpreted) với kiểu dữ liệu động (dynamic typing). Nghĩa là bạn viết code rồi chạy trực tiếp, không có bước biên dịch riêng như C++ hay Java, và một biến có thể giữ số ở dòng này rồi giữ chuỗi ở dòng sau. Tư duy chạy code của bạn từ Node.js chuyển sang Python gần như không phải đổi gì.
Khác biệt đáng để bạn quan tâm ngay chỉ có một: Python chạy chậm hơn JavaScript đáng kể ở các phép tính thuần trong vòng lặp. Đổi lại, nó gắn cực kỳ tốt với các thư viện viết bằng C/C++ — và như mục dưới sẽ thấy, đó chính là lý do toàn bộ ngành AI chọn Python dù nó chậm.
Phần nội tại bên dưới không cần thiết để làm được bài này. Nó ở đây cho bạn nào tò mò, và có thể bỏ qua hoàn toàn mà không ảnh hưởng gì tới các bài sau.
Trình thông dịch Python mặc định tên là CPython (viết bằng C, nên có chữ C). Nó cũng dịch code thành bytecode — bạn sẽ thấy các tệp
.pyc xuất hiện trong thư mục
__pycache__ sau lần chạy đầu — nhưng không có bước dịch tiếp thành mã máy tối ưu.
Bytecode được thực thi tuần tự trên một máy ảo. Đó là gốc rễ của việc Python chậm hơn ở vòng lặp thuần.
Tuy nhiên, tại sao Python vẫn thống trị mảng AI/Deep Learning vốn cực kỳ ngốn CPU/GPU? Đó là nhờ các thư viện tính toán tensor nền tảng như NumPy và PyTorch không thực sự được viết bằng Python — phần lõi của chúng viết bằng C++ và CUDA (ngôn ngữ của NVIDIA để chạy tính toán trên card đồ hoạ). Khi PyTorch nhân hai ma trận, nó đẩy toàn bộ dữ liệu xuống tầng mã máy C++ chạy đa luồng bên ngoài sự quản lý của CPython. Nên phép tính đó lách hoàn toàn qua khoá GIL.
Nói cách khác: Python trong AI không làm việc tính toán. Nó chỉ ra lệnh. Việc nặng do C++/CUDA làm, và GIL không với tới đó được. Đây là lý do một ngôn ngữ "chậm" lại là ngôn ngữ của ngành AI.
Tensor chỉ là một mảng số nhiều chiều. Hết. Không có gì huyền bí. Bạn đã dùng nó trong JavaScript rồi, chỉ là chưa gọi tên:
5— một số lẻ. Tensor 0 chiều (còn gọi là scalar).[1, 2, 3]— một mảng phẳng. Tensor 1 chiều (vector).[[1, 2], [3, 4]]— mảng của mảng. Tensor 2 chiều (matrix, ma trận).- Mảng 3 chiều, 4 chiều… vẫn là tensor. Số chiều gọi là rank.
Hai ví dụ để thấy ranh giới. Bảng điểm 100 học sinh × 5 môn là một tensor 2 chiều: toàn số, kích thước đều nhau, nên nhân/cộng ma trận được. Còn một danh sách 100 hồ sơ kiểu
{ name: "An", age: 20, note: "giỏi" } không phải tensor: nó chứa chữ, và các phần
tử không cùng dạng. Muốn dùng được trong AI, ta phải chuyển nó thành số trước — và đó chính là việc mà
các bài về embeddings (Bài 8) làm. Vì sao AI chỉ nói chuyện bằng tensor? Vì card đồ hoạ được thiết kế để làm một việc cực nhanh: cộng và nhân những khối số đều đặn, hàng nghìn ô cùng lúc. Mọi thứ trong deep learning — ảnh, câu văn, âm thanh — đều bị quy về tensor để dùng được sức mạnh đó.
-
npm vs pip:
npm installtương đương vớipip install. Kho lưu trữ PyPI (Python Package Index) đóng vai trò tương tự npmjs.org. -
package.json vs requirements.txt: Thay vì liệt kê dependencies trong file JSON,
Python sử dụng file text đơn giản
requirements.txtchứa danh sách các thư viện kèm phiên bản (ví dụ:numpy==1.24.3). -
node_modules vs venv (Virtual Environment): Đây là điểm khác biệt lớn. Node.js tự
động tạo thư mục
node_modulescục bộ trong thư mục dự án để cô lập dependencies. Python thì mặc định cài đặt thư viện vào thư mục hệ thống toàn cục (global package). Để tránh xung đột phiên bản giữa các dự án khác nhau (dependency hell), bạn bắt buộc phải tạo môi trường ảo độc lập bằng công cụvenv.
Cơ chế của
venv chỉ đơn giản là tạo ra một bản sao gọn nhẹ của trình thông dịch Python và
sửa đổi các biến môi trường PATH và VIRTUAL_ENV của shell để trình thông dịch
ưu tiên tìm kiếm thư viện trong thư mục venv cục bộ của dự án trước (thay vì tìm trong
sys.path toàn cục).
Để khởi tạo một môi trường ảo cục bộ trong thư mục dự án của bạn (tương đương với việc có một thư mục cô lập dependencies riêng), hãy chạy các lệnh sau trong terminal:
# The word "venv" appears TWICE here, in two different roles:
# -m venv = run Python's standard module named `venv`
# venv = name of the DIRECTORY to create (.venv or env work just as well)
python3 -m venv venv
# Activate on macOS / Linux
source venv/bin/activate
# Activate on Windows (PowerShell)
# venv\Scripts\Activate.ps1
# Sign it worked: your shell prompt gains a (venv) prefix
# (venv) you@machine myproject %
# A stronger check — the path must point inside your project's venv:
which python # macOS/Linux -> .../myproject/venv/bin/python
# where python # Windows
# From now on, pip installs into venv/ instead of the system
pip install numpy pandas
# When you are done
deactivate
source venv/bin/activate. Đây không phải lỗi —
kích hoạt chỉ là sửa biến môi trường của đúng phiên terminal đó. Triệu chứng khi quên:
ModuleNotFoundError: No module named 'numpy' dù bạn nhớ rõ mình đã cài numpy hôm qua.
Hai: đừng commit thư mục venv lên git. Nó là bản sao của trình thông dịch cộng toàn bộ thư viện, nặng hàng trăm MB và chỉ chạy được trên đúng máy của bạn. Thêm
venv/ vào
.gitignore — vai trò của nó giống hệt node_modules/. Ba: cách "chia sẻ" thư viện cho người khác. Vì venv không được commit, bạn xuất danh sách thư viện ra file:
pip freeze > requirements.txt. Người khác clone dự án về sẽ tạo
venv của riêng họ rồi chạy pip install -r requirements.txt. Đây chính là cặp
package.json + npm install mà bạn đã quen, chỉ khác tên.
| Khái niệm | JavaScript (Node.js) | Python (CPython) |
|---|---|---|
| Quản lý thư viện | npm (Node Package Manager) | pip (Package Installer for Python) |
| Cô lập thư viện | node_modules (mặc định tạo cục bộ) | venv / conda (phải kích hoạt môi trường ảo chủ động) |
| Tệp khai báo thư viện | package.json | requirements.txt |
| Cơ chế thực thi | JIT Compilation (V8 Engine) | Interpreter / Bytecode VM |
2. Kiểu dữ liệu & Cơ chế tham chiếu qua lăng kính JS
Cú pháp tối thiểu để đọc được mọi đoạn code trong series này
Trước khi nói về kiểu dữ liệu, cần dọn ba khác biệt cú pháp sẽ khiến bạn dừng lại ngay ở dòng code Python đầu tiên. Đây không phải mẹo hay — thiếu chúng thì các đoạn code phía sau đọc không nổi.
Một: Python không có dấu ngoặc nhọn. Khối lệnh được xác định bằng
thụt lề. Chỗ nào trong JavaScript bạn viết { thì Python viết dấu hai chấm
: rồi thụt vào; chỗ nào đóng } thì Python chỉ cần thụt ra. Điều này không phải
quy ước cho đẹp mà là cú pháp: thụt lề sai là lỗi chương trình, không phải lỗi thẩm mỹ.
// ===== JavaScript =====
function greet(name) {
if (name) {
console.log(`Hello ${name}`);
} else {
console.log("Hello there");
}
}
const scores = [8, 9, 10];
for (const s of scores) {
console.log(s);
}
# ===== Python — same logic, not a single curly brace =====
def greet(name): # JS `{` becomes a colon `:`
if name:
print(f"Hello {name}") # console.log becomes print
else:
print("Hello there")
# no `}` — dedenting is what ends the block
scores = [8, 9, 10]
for s in scores:
print(s)
Hai: print() thay cho console.log(), và f-string thay cho template
literal.
Trong JavaScript bạn nhúng biến bằng dấu backtick và ${name}. Python làm y hệt nhưng đặt chữ
f ngay trước dấu nháy và dùng ngoặc nhọn trần: f"Hello {name}". Quên chữ
f là lỗi rất hay gặp — lúc đó Python in ra nguyên văn chuỗi Hello {name} thay vì
giá trị, và nó không báo lỗi gì cả. Bạn sẽ thấy f-string được dùng trong dự án thực hành ở mục 4.
Ba: bốn kiểu dữ liệu tập hợp, viết ra trông thế nào. Bảng đối chiếu ở dưới nói về bản
chất bên trong, nhưng trước hết hãy xem mặt chữ của chúng — đặc biệt là cách truy cập dict,
vì đây là chỗ khác JavaScript nhất:
# LIST — like a JS Array. Mutable, ordered.
scores = [8, 9, 10]
scores.append(7) # JS: scores.push(7)
print(len(scores)) # JS: scores.length -> 4
# DICT — like a JS Object/Map.
# But you INDEX IT WITH BRACKETS, never with a dot!
student = {"name": "An", "age": 20}
print(student["name"]) # 'An' ← correct
# print(student.name) # AttributeError! Python dicts have no dot syntax
student["grade"] = "12A" # adding a new key
# TUPLE — like a list but FROZEN. Written with parentheses.
point = (10.5, 20.3)
# point[0] = 99 # TypeError: cannot reassign
x, y = point # unpacking into two variables — very common in Python
# SET — unique items, no order. Like a JS Set.
tags = {"ai", "python", "ai"}
print(tags) # {'ai', 'python'} — the duplicate is dropped
{} rỗng là dict, không phải setdict và set, nên trường hợp rỗng bị nhập nhằng — và
Python giải quyết bằng cách ưu tiên dict. x = {} tạo ra một dict rỗng, chứ
không phải set rỗng. Muốn set rỗng phải viết x = set(). Kiểm tra nhanh bằng
type(x) — hàm này trả về kiểu của biến và sẽ là công cụ chẩn đoán bạn
dùng nhiều nhất trong tuần đầu học Python.
Mutable và Immutable — nền tảng của mọi lỗi dữ liệu ngầm
Cú pháp dữ liệu của Python rất giống JavaScript, tuy nhiên Python phân biệt rạch ròi giữa các kiểu dữ liệu có thể thay đổi (Mutable) và không thể thay đổi (Immutable) ngay từ lõi thiết kế để tối ưu hóa hiệu năng bộ nhớ. Sự phân biệt này không phải chuyện học thuật: nó là nguyên nhân trực tiếp của hai cạm bẫy ở cuối mục này, và cả hai đều thuộc loại "không báo lỗi, chỉ cho kết quả sai".
Giờ nhìn sâu hơn vào bốn kiểu vừa xem, lần này ở góc độ bên trong chúng hoạt động thế nào:
- List (danh sách tuần tự): Tương đương mảng Array trong JS. Bản chất bên dưới của List là một mảng động (Dynamic Array) tự động mở rộng kích thước (over-allocation) khi đầy để tránh phải cấp phát bộ nhớ lại liên tục.
- Dict (Dictionary): Dạng bảng băm key-value, tương đương Object/Map của JS. Từ Python 3.6 trở đi, Dict được cấu trúc tối ưu hóa bộ nhớ và bảo toàn thứ tự chèn phần tử bằng cách sử dụng một mảng chỉ mục 1D gọn nhẹ song song với mảng lưu trữ giá trị thực.
-
Tuple: Một dạng danh sách cố định chỉ đọc (Immutable List) được bọc bởi dấu ngoặc đơn
(val1, val2). Tuple do tính bất biến nên có hiệu năng truy xuất nhanh hơn List và có thể được dùng làm khóa (key) trong Dict (vì giá trị của nó có mã hash cố định), điều mà List không làm được.
.map() hay .filter(), hệ
thống luôn khởi tạo một mảng mới trung gian hoàn toàn trên bộ nhớ RAM. Ở Python, ngoài List Comprehension
[x**2 for x in data] (tạo mảng mới), chúng ta có
Generator Expression bằng cách thay ngoặc vuông bằng ngoặc đơn:
(x**2 for x in data). Generator sử dụng cơ chế
đánh giá lười (Lazy Evaluation), chỉ tính toán giá trị tiếp theo khi được yêu cầu
(yield). Điều này giúp xử lý hàng triệu bản ghi dữ liệu mà không tốn thêm một byte RAM nào, cực kỳ tối
ưu cho các luồng nạp dữ liệu (data pipelines) của AI.
b = a rồi thay đổi giá trị trong
b, dẫn đến việc dữ liệu gốc trong a bị sửa đổi ngầm mà không hề biết.
Hãy xem ví dụ đối chiếu trực quan dưới đây để hiểu cách sao chép dữ liệu an toàn:
# ❌ WRONG: plain assignment shares the reference, so the original changes too
list_a = [1, 2, 3]
list_b = list_a # copies only the memory address, not the contents
list_b.append(4)
print(list_a) # [1, 2, 3, 4] — the original was modified behind your back!
# ✅ RIGHT: make a shallow copy with .copy()
list_c = [1, 2, 3]
list_d = list_c.copy()
list_d.append(4)
print(list_c) # [1, 2, 3] — the original is safe
Đối với các đối tượng từ điển phức tạp chứa nhiều tầng lồng nhau (nested dictionary), bạn bắt buộc phải sử
dụng thư viện chuẩn copy và hàm deepcopy() để nhân bản độc lập toàn bộ các vùng
nhớ con:
import copy
# A nested structure: the dict holds a list inside it
original = {"name": "AI Model", "layers": [128, 64, 32]}
# deepcopy clones every level, not just the outer dict
cloned = copy.deepcopy(original)
cloned["layers"].append(16)
print(original["layers"]) # [128, 64, 32] — untouched
print(cloned["layers"]) # [128, 64, 32, 16] — changed independently
function f(arr = []) {"{"}...{"}"} được TẠO MỚI mỗi lần hàm được gọi. Trong Python, biểu
thức mặc định (def f(arr=[]):) chỉ được đánh giá ĐÚNG MỘT LẦN, tại thời
điểm hàm được định nghĩa — không phải mỗi lần gọi. Vì List là Mutable, mọi lần gọi hàm sau đó đều dùng
CHUNG một list đó, tích luỹ dữ liệu từ lần gọi trước.
# ❌ WRONG: the default list is "remembered" across separate calls
def add_log(entry, history=[]):
history.append(entry)
return history
print(add_log("request-1")) # ['request-1'] — looks fine
print(add_log("request-2")) # ['request-1', 'request-2'] — WRONG! expected ['request-2']
# history=[] is created ONCE when def runs; every later call reuses that same list
# ✅ RIGHT: default to None, then build a fresh list inside the body
def add_log_fixed(entry, history=None):
if history is None:
history = [] # a brand-new list on every call
history.append(entry)
return history
print(add_log_fixed("request-1")) # ['request-1']
print(add_log_fixed("request-2")) # ['request-2'] — correct, independent of the last call
Lỗi này đặc biệt nguy hiểm trong code xử lý dữ liệu AI vì nó thường "im lặng" — không ném exception nào cả, chỉ âm thầm tích luỹ dữ liệu rác qua nhiều lần gọi hàm (ví dụ một hàm tiền xử lý batch dữ liệu dùng list mặc định để "cache" kết quả tạm), khiến kết quả huấn luyện sai lệch mà rất khó truy ra nguyên nhân gốc.
3. Lập trình bất đồng bộ: Asyncio vs Event Loop
Trong JavaScript, lập trình bất đồng bộ (Asynchronous Programming) là cơ chế mặc định. Trình duyệt hoặc Node.js chạy một vòng lặp sự kiện (Event Loop) liên tục ở tầng nền để xử lý các sự kiện click, truy vấn mạng hoặc đọc file không chặn (non-blocking) thông qua Call Stack, Microtask Queue (Promises) và Macrotask Queue.
Trái lại, Python về bản chất là ngôn ngữ đồng bộ tuần tự (Synchronous - Blocking). Khi chạy phép toán
nặng, luồng bị chặn hoàn toàn. Để chạy bất đồng bộ, Python có thư viện chuẩn asyncio, xây
trên một thứ gọi là coroutine.
Coroutine là gì? Nói gọn: một hàm có thể tạm dừng giữa đường để nhường chỗ cho
việc khác, rồi quay lại tiếp tục đúng chỗ đã dừng. Trong Python bạn tạo nó bằng async def, và
điểm tạm dừng là chỗ có từ khoá await. Bạn đã dùng đúng khái niệm này trong JavaScript rồi —
async function của JS cũng là một coroutine, chỉ khác tên gọi.
Trong JavaScript, gọi
fetchData() là hàm bắt đầu chạy ngay; bạn nhận về
một Promise đại diện cho công việc đang diễn ra. Trong Python, gọi fetch_data()
không chạy một dòng nào trong thân hàm. Nó trả về một đối tượng coroutine —
như một tờ phiếu ghi "việc cần làm", nằm im cho tới khi có ai đó thực sự đem đi thực hiện. Ai đem đi thực hiện? Hoặc
await (nếu bạn đang ở trong một coroutine khác), hoặc
asyncio.run() ở cấp ngoài cùng. Đó chính là nghĩa cụ thể của câu "Event Loop trong Python không tự chạy ngầm": JavaScript luôn có một Event Loop sẵn sàng nhận việc, còn Python thì bạn phải tự dựng nó lên bằng
asyncio.run(). Dấu hiệu nhận biết khi mắc lỗi này: Python in ra cảnh báo
RuntimeWarning: coroutine 'fetch_data' was never awaited và hàm của bạn im lặng không chạy.
Thấy dòng đó nghĩa là bạn quên await hoặc quên asyncio.run().
import asyncio
async def fetch_data():
print("Fetching data...")
return 42
# ❌ Called the JavaScript way: NOTHING is printed
result = fetch_data()
print(result) # <coroutine object fetch_data at 0x104f...> ← just the "ticket"
# ✅ The ticket has to be handed to someone who runs it
result = asyncio.run(fetch_data())
print(result) # prints "Fetching data..." then 42
time.sleep(5)) bên trong một hàm async của Python, toàn bộ Event Loop của
asyncio sẽ bị treo cứng. Mọi tác vụ async chạy song song khác sẽ bị đóng băng. Để tránh điều này, bạn
phải sử dụng các thư viện async tương thích (như aiohttp cho mạng), hoặc chuyển tác vụ nặng
sang chạy ở thread riêng biệt.
Cách khắc phục cụ thể (từ Python 3.9 trở lên): dùng asyncio.to_thread() để đẩy hàm chặn luồng
sang một thread nền, giải phóng Event Loop tiếp tục xử lý các tác vụ async khác trong lúc chờ:
import asyncio
import time
def cpu_heavy_task():
time.sleep(3) # stands in for heavy CPU work, or an old library with no async API
return "Heavy work done"
async def ticker():
for i in range(5):
print(f"Tick {i} — the Event Loop is still running...")
await asyncio.sleep(0.5)
async def main():
# ❌ Calling cpu_heavy_task() directly here would freeze ticker() for 3 seconds.
# ✅ Hand it to a separate thread; the Event Loop stays free to run ticker():
result_task = asyncio.create_task(asyncio.to_thread(cpu_heavy_task))
ticker_task = asyncio.create_task(ticker())
result = await result_task
print(result)
await ticker_task
asyncio.run(main())
# Output: "Tick 0..4" keeps printing every 0.5s while cpu_heavy_task() runs for 3s
Hãy cùng so sánh cấu trúc mã viết async/await giữa JavaScript và Python:
// JavaScript version (Node.js)
const delay = (ms) => new Promise(resolve => setTimeout(resolve, ms));
async function fetchMetadata() {
console.log("Download starting...");
await delay(1000); // non-blocking wait
console.log("Download finished.");
return { status: 200 };
}
// Just call it — JS already has an Event Loop running in the background
fetchMetadata();
# Python version (asyncio)
import asyncio
async def fetch_metadata():
print("Download starting...")
await asyncio.sleep(1) # non-blocking wait, the equivalent of delay()
print("Download finished.")
return {"status": 200}
# You must start the Event Loop yourself
result = asyncio.run(fetch_metadata())
4. Dự án thực hành: Công cụ thu thập và chuẩn hóa dữ liệu thô
Để biến lý thuyết thành thực tiễn, chúng ta sẽ xây dựng một script Python hoàn chỉnh chạy trong môi trường
ảo venv. Script làm ba việc theo thứ tự: tự tạo một tệp
raw_feedback.json chứa vài phản hồi thô của người dùng (bạn không cần chuẩn bị file đầu vào
nào — script tự sinh ra để bài này chạy được ngay), đọc lại tệp đó rồi làm sạch từng dòng văn bản, và ghi
kết quả ra cleaned_feedback.csv.
Việc "làm sạch" ở đây rất đơn giản — cắt khoảng trắng thừa hai đầu và chuyển hết về chữ thường — nhưng cấu trúc ba bước đọc → biến đổi → ghi chính là bộ xương của mọi data pipeline mà bạn sẽ gặp lại suốt series, kể cả khi dữ liệu là hàng triệu tài liệu cho hệ thống RAG ở Bài 14.
import json
import csv
import os
def clean_text(text):
"""
Basic text cleaning: trim surrounding whitespace and lowercase.
"""
# Real-world data always contains empty cells and wrong types. A text helper
# that does not guard against them breaks the pipeline at record 10,000.
if not isinstance(text, str):
return ""
return text.strip().lower()
def run_cleaning_pipeline(input_path, output_path):
print("--- Starting the data cleaning pipeline ---")
if not os.path.exists(input_path):
print(f"Error: file {input_path} does not exist.")
return
# `with` closes the file on the way out, even if an error is raised inside
# the block. It is Python's version of the try/finally you write by hand in
# JavaScript. And encoding="utf-8" is not optional: drop it and accented
# text turns into garbage characters on Windows.
with open(input_path, "r", encoding="utf-8") as file:
raw_data = json.load(file)
cleaned_records = []
for index, record in enumerate(raw_data):
# .get(key, fallback) returns the fallback instead of raising when the
# key is missing — the safe way to read data that came from outside.
user_id = record.get("id", index)
raw_comment = record.get("comment", "")
cleaned_records.append({
"user_id": user_id,
"cleaned_comment": clean_text(raw_comment)
})
# Write the cleaned rows out as CSV, ready for the NLP lessons later on.
with open(output_path, "w", newline="", encoding="utf-8") as csv_file:
fieldnames = ["user_id", "cleaned_comment"]
writer = csv.DictWriter(csv_file, fieldnames=fieldnames)
writer.writeheader()
writer.writerows(cleaned_records)
print(f"Done. Wrote {len(cleaned_records)} cleaned records to {output_path}")
# Everything below runs only when this file is executed directly,
# not when it is imported from another module.
if __name__ == "__main__":
# The sample input is generated here so the lesson runs with no setup.
# These comments stay in Vietnamese on purpose: they are the DATA being
# cleaned, and they demonstrate why encoding="utf-8" matters above.
mock_data = [
{"id": 101, "comment": " Mô hình AI chạy RẤT NHANH! "},
{"id": 102, "comment": "Tôi Cần hỗ Trợ kỹ thuật gấp... "},
{"id": 103, "comment": " Tuyệt VỜI, 10 điểm. "}
]
with open("raw_feedback.json", "w", encoding="utf-8") as f:
json.dump(mock_data, f, ensure_ascii=False, indent=2)
run_cleaning_pipeline("raw_feedback.json", "cleaned_feedback.csv")
Khối code trên dài, nhưng chỉ có ba chỗ đáng để bạn dừng lại đọc kỹ. Thứ nhất là
if not isinstance(text, str): return "" trong clean_text — dữ liệu thật luôn có
ô trống hoặc sai kiểu, và một hàm xử lý văn bản không tự vệ sẽ làm vỡ cả pipeline ở đúng bản ghi thứ
10.000. Thứ hai là with open(...) as f: từ khoá with tự đóng file khi ra khỏi
khối, kể cả khi có lỗi xảy ra giữa đường — nó là bản Python của try/finally mà bạn hay viết
tay trong JavaScript. Thứ ba là encoding="utf-8" ở mọi lệnh mở file: bỏ nó đi thì tiếng Việt
có dấu sẽ thành ký tự rác trên Windows, và đây là lỗi tốn thời gian một cách vô ích nhất khi xử lý dữ liệu
tiếng Việt.
Để chạy dự án thực hành này trên máy tính của bạn, hãy thực hiện theo đúng các bước quy chuẩn sau:
-
Mở terminal tại thư mục chứa file code và khởi tạo môi trường ảo:
python3 -m venv venv. -
Kích hoạt môi trường ảo:
source venv/bin/activate(đối với macOS/Linux) hoặcvenv\Scripts\activate(đối với Windows). - Chạy script Python:
python data_cleaner.py. -
Kiểm tra thư mục dự án. Bạn sẽ thấy hai tệp mới:
raw_feedback.json— dữ liệu thô do chính script tự sinh ra ở bước đầu — vàcleaned_feedback.csvchứa các bình luận đã được cắt khoảng trắng và chuyển về chữ thường. Mở cả hai ra so sánh cạnh nhau là cách nhanh nhất để thấy đúng những gì hàmclean_textđã làm.
Tóm tắt bài học & Cầu nối kiến thức
- Đạt được: Làm chủ cú pháp Python cơ bản và sự khác biệt về kiểu dữ liệu so với JavaScript.
-
Đạt được: Biết cách thiết lập môi trường ảo
venvvà quản lý thư viện bằngpipthay thế chonpm/node_modules.
Cầu nối bài tiếp theo: Có được tư duy viết mã Python, bài học tiếp theo sẽ đưa ta bước vào thế giới toán học nền tảng của Trí tuệ nhân tạo: các phép toán Vector, Ma trận và Đạo hàm được lập trình trực quan bằng Python thuần túy.
Tải file code thực hành minh họa bài học
File Python data_cleaner.py — script xử lý và chuẩn hóa dữ liệu mẫu dùng trong bài học này
(chạy python data_cleaner.py, không cần cài thêm gì):
Bình luận