Chúng ta đã tìm hiểu thuật toán kNN đơn giản qua hai bài viết:
- https://ngocminhtran.com/2020/02/16/hoc-python-qua-cac-thuat-toan-machine-learning-co-ban-thuat-toan-k-nearest-neighbors-knn/
- https://ngocminhtran.com/2020/03/04/hoc-python-qua-cac-thuat-toan-machine-learning-co-ban-thuat-toan-k-nearest-neighbors-knn-tt/
Trong bài này chúng ta tiếp tục khám phá ngôn ngữ Python thông qua thuật toán kNN. Lần này là ví dụ phức tạp hơn về thuật toán này. Toàn bộ nội dung của thuật toán này được tham khảo từ chương trình chương 2, mục 2.2 (Example: improving matches from a dating site with kNN), cuốn sách Machine Learning in Action https://www.manning.com/books/machine-learning-in-action. Toàn bộ mã nguồn và các tập tin liên quan của chương 2 có thể xem và tải về tại https://github.com/TranNgocMinh/machinelearninginaction/tree/master/Ch02 Tất nhiên, bạn cũng cần một ít điều chỉnh để phù hợp với các phiên bản Python.
Trong bài viết này, chúng ta cùng tìm hiểu hàm file2matrix (https://github.com/TranNgocMinh/machinelearninginaction/blob/master/Ch02/kNN.py) nhận dữ liệu từ tập tin, cụ thể là tập tin datingTestSet.txt (https://github.com/TranNgocMinh/machinelearninginaction/blob/master/Ch02/datingTestSet.txt) , và chuyển sang các ma trận và vec tơ. Dữ liệu từ tập tin datingTestSet.txt có 1000 dòng với 5 dòng đầu tiên:
40920 8.326976 0.953952 largeDoses
14488 7.153469 1.673904 smallDoses
26052 1.441871 0.805124 didntLike
75136 13.147394 0.428964 didntLike
38344 1.669788 0.134296 didntLike
…
Bỏ qua tính chất ý nghĩa của các dữ liệu này vì mục đích của chúng ta là khám phá cú pháp và các kỹ thuật lập trình với Python, để nhận dữ liệu từ một tập tin txt nói chung hay cụ thể là datingTestSet.txt, chúng ta:
- Mở tập tin cần nhận (hay đọc) dữ liệu dùng hàm open()
fr = open('datingTestSet.txt')
- Đọc các dòng dữ liệu dùng hàm readlines()
arrayOLines = fr.readlines()
Tới đây, chúng ta có thể xem nội dung nhận được từ biến arrayOLines bằng lệnh print(arrayOLines):
[‘40920\t8.326976\t0.953952\tlargeDoses\n’, ‘14488\t7.153469\t1.673904\tsmallDoses\n’, ‘26052\t1.441871\t0.805124\tdidntLike\n’, ‘75136\t13.147394\t0.428964\tdidntLike\n’, ‘38344\t1.669788\t0.134296\tdidntLike\n’, ‘72993\t10.141740\t1.032955\tdidntLike\n’, ‘35948\t6.830792\t1.213192\tlargeDoses\n’, ‘42666\t13.276369\t0.543880\tlargeDoses\n’,…
Rõ ràng, với dữ liệu như thế này chúng ta chưa thể làm được gì, vẫn cần một vài bước xử lý trước khi có thể sử dụng (trong machine learning là dùng để huấn luyện và kiểm thử mô hình). Mặc dù chưa sử dụng được ngay nhưng chúng ta có thể biết được tập tin có bao nhiêu hàng (như đã nêu trên là 1000) bằng cách dùng hàm len():
numberOfLines = len(arrayOLines)
Mục đích của chúng ta là làm sạch dữ liệu với những các ký tự xuống dòng ‘\n’ hay các ký tự khoảng cách ‘\t’ và tách 3 cột đầu tiên đặt trong một ma trận và cột cuối cùng đặt trong một vector (cũng là một ma trận nhưng 1 chiều). Như dữ liệu mẫu thu được từ biến arrayOLines, chúng ta cần thực hiện 2 bước quan trọng:
- Xóa những ký tự xuống dòng ‘\n’ trên mỗi hàng dùng hàm strip()
- Tách các dữ liệu bằng một khoảng trắng dựa vào dấu hiệu là các ký tự ‘\t’ dùng hàm split(‘\t’)
Đoạn mã thực hiện các nhiệm vụ này:
for line in arrayOLines: line = line.strip() listFromLine = line.split('\t')
Chúng ta duyệt qua từng hàng và thực hiện hai nhiệm vụ trên với vòng lặp for..in (đã đề cập tại cuối bài https://ngocminhtran.com/2020/03/04/hoc-python-qua-cac-thuat-toan-machine-learning-co-ban-thuat-toan-k-nearest-neighbors-knn-tt/ ). Xem kết quả nhận được từ listFromline bằng cách thêm lệnh print(listFromLine) đến for:
for line in arrayOLines: line = line.strip() listFromLine = line.split('\t') print(listFromLine)
Kết quả:
[‘40920’, ‘8.326976’, ‘0.953952’, ‘largeDoses’]
[‘14488’, ‘7.153469’, ‘1.673904’, ‘smallDoses’]
[‘26052’, ‘1.441871’, ‘0.805124’, ‘didntLike’]
[‘75136’, ‘13.147394’, ‘0.428964’, ‘didntLike’]
[‘38344’, ‘1.669788’, ‘0.134296’, ‘didntLike’]
…
Giả sử chúng ta muốn hiển thị 3 cột đầu tiên từ mỗi listFromLine thì cần lưu ý rằng:
- Mỗi hàng từ listFromLine có kiểu list trong Python
- Mỗi giá trị trong list được quản lý theo các chỉ số từ 0 giống kiểu mảng
- Hiển thị một danh sách con (sub list) từ listFromLine dùng cú pháp listFromLine[start:end+1], với start là chỉ số bắt đầu và end là chỉ số cuối (lưu ý phải cộng thêm 1). Ví dụ hiển thị 3 giá trị đầu tiên từ mỗi listFromLine
print(listFromLine[0:3]) # giá trị đầu tiên tại chỉ số 0, # giá trị thứ 3 có chỉ số 2 và cộng thêm 1
- Hiển thị giá trị cuối cùng từ list dùng chỉ số -1, ví dụ listFromLine[-1] thì hàng đầu tiên sẽ nhận largeDoes
Với các lưu ý trên, chúng ta viết lại for:
for line in arrayOLines: line = line.strip() listFromLine = line.split('\t') print(listFromLine[0:3])
Kết quả:
[‘40920’, ‘8.326976’, ‘0.953952’]
[‘14488’, ‘7.153469’, ‘1.673904’]
[‘26052’, ‘1.441871’, ‘0.805124’]
[‘75136’, ‘13.147394’, ‘0.428964’]
[‘38344’, ‘1.669788’, ‘0.134296’]
…
Thay thành chỉ số 0:3 thành -1:
largeDoses
smallDoses
didntLike
didntLike
didntLike
…
Vì dữ liệu từ cột cuối cùng là các chuỗi ký tự trong khi 3 cột đầu tiên là số nên để tiện việc tính toán sau này chúng ta sẽ gán các chuỗi didntLike, largeDoses và smallDoses tương ứng với các giá trị số bằng cách dùng cấu trúc dictionary (xem lại bài https://ngocminhtran.com/2020/03/04/hoc-python-qua-cac-thuat-toan-machine-learning-co-ban-thuat-toan-k-nearest-neighbors-knn-tt/ ) như sau:
love_dictionary={'largeDoses':3, 'smallDoses':2, 'didntLike':1}
Bây giờ, chúng ta sẽ đặt toàn bộ dữ liệu từ 3 cột đầu tiên của mỗi listFromLine vào trong một ma trận các danh sách tên returnMat như sau:
index = 0 returnMat = zeros((numberOfLines,3)) for line in arrayOLines: line = line.strip() listFromLine = line.split('\t') returnMat[index,:] = listFromLine[0:3] index += 1
Biến index đại diện cho từng hàng của ma trận bắt đầu với hàng đầu tiên có chỉ số 0 và ma trận returnMat được khởi tạo bằng ma trận các giá trị 0 với 1000 hàng X 3 cột dùng hàm zeros từ thư viện NumPy:
index = 0 returnMat = zeros((numberOfLines,3))
Giá trị returnMat:
[[ 0. 0. 0.]
[ 0. 0. 0.]
[ 0. 0. 0.]
…,
[ 0. 0. 0.]
[ 0. 0. 0.]
[ 0. 0. 0.]]
Biến returnMat lúc này tương tự biến group sau khi dùng phương thức array trong bài https://ngocminhtran.com/2020/02/16/hoc-python-qua-cac-thuat-toan-machine-learning-co-ban-thuat-toan-k-nearest-neighbors-knn/
Trong for, mỗi hàng với 3 cột đầu tiên từ listFromLine sẽ được gán lần lượt đến từng hàng của ma trận returnMat:
returnMat[index,:] = listFromLine[0:3]
Cú pháp [index,:] dùng để truy cập đến hàng index với tất cả các cột trên hàng đó. Để dễ hiểu, chúng ta xét một ma trận đơn giản hơn như sau:
mat = array([[3,2],[1,1]])
Kết quả khi dùng lệnh print(mat):
[[3 2]
[1 1]]
Chúng ta có ma trận 2 hàng x 2 cột. Muốn xem dữ liệu hàng đầu tiên:
mat[0,:] # [3 2]
Xem giá trị cột thứ 2 hàng đầu tiên:
mat[0,1] # 2
Xem giá trị cột thứ hai:
mat[:,1] # [2 1]
Sau khi đặt dữ liệu 3 cột đầu tiên trong ma trận returnMat, kế tiếp, chúng ta sẽ đặt dữ liệu cột cuối cùng trong một vectơ(ma trận kích cỡ 1000 x 1) tên classLabelVector bằng cách thêm vài dòng mã như sau:
love_dictionary={'largeDoses':3, 'smallDoses':2, 'didntLike':1} index = 0 returnMat = zeros((numberOfLines,3)) classLabelVector = [] for line in arrayOLines: line = line.strip() listFromLine = line.split('\t') returnMat[index,:] = listFromLine[0:3] classLabelVector.append(love_dictionary.get(listFromLine[-1])) index += 1
Biến love_dictionary chúng ta đã khai báo ở trên, classLabelVector khởi tạo bởi một mảng rỗng và dùng hàm append để thêm các giá trị đến mảng. Hàm get từ love_dictionary có thể xem lại cấu trúc dictionary từ bài https://ngocminhtran.com/2020/03/04/hoc-python-qua-cac-thuat-toan-machine-learning-co-ban-thuat-toan-k-nearest-neighbors-knn-tt/
Như vậy chúng ta đã hoàn thành việc nhận dữ liệu và đặt chúng vào một ma trận và một vectơ để có thể sử dụng cho các bước tiếp theo sẽ được tìm hiểu trong bài kế tiếp.
Ý kiến bài viết