Lời nói đầu

Đây là bài viết đầu tiên trong loạt bài viết nhập môn ngôn ngữ Python thông qua các thuật toán Machine Learning cơ bản. Ngôn ngữ Python là một trong những ngôn ngữ phổ biến nhất hiện nay và có thể dễ dàng tìm thấy vô số tài liệu về ngôn ngữ này.

Tôi đã viết nhiều hướng dẫn (vừa viết vừa học) các ngôn ngữ lập trình theo kiểu tutorial hay step-by-step như JavaScript, Java, C# và khi muốn viết về Python theo kiểu viết những ngôn ngữ này thì thấy không cảm hứng và muốn viết theo một kiểu mới. Học Machine Learning luôn mang lại cho tôi nhiều hứng thú nhất và, tất nhiên, cũng gây không ít khó khăn. Loạt bài viết này sẽ cung cấp các đoạn mã chương trình hoàn chỉnh từ các nguồn tham khảo sẽ được giới thiệu trong mục kế tiếp và giải thích cú pháp Python được sử dụng trong các chương trình này. Học ngôn ngữ lập trình từ source code các chuyên gia cũng là một cách học hiệu quả.

Một điều nữa cần lưu ý trước khi đọc các bài viết này, người đọc cần một ít kiến thức về lập trình hay kinh nghiệm với một vài ngôn ngữ như C, C#, v.v. trước đó. Nếu là một người lần đầu tiếp cận với ngôn ngữ lập trình thì loạt bài viết này không dành cho bạn.

Và cuối cùng cũng xin lưu ý, các bài viết này dùng các chương trình, thuật toán Machine Learning nhưng là chủ đề về ngôn ngữ Python chứ không phải Machine Learning. Machine Learning sẽ được đề cập sâu hơn trong các bài viết khác. Tất nhiên, trong quá trình giải thích cú pháp ngôn ngữ Python sẽ đụng chạm một ít về thuật toán Machine Learning liên quan nhưng sẽ không chi tiết, sâu sắc như một bài viết chuyên về Machine Learning.

Tham khảo

Bài viết trên đây được tham khảo từ chương trình chương 2, mục 2.1, cuốn sách Machine Learning in Action. Bài viết này chỉ giới thiệu một phần chương trình và giảng giải cách dùng Python. Phần còn lại và các vấn đề khác của thuật toán kNN trong chương 2 của cuốn sách sẽ được giới thiệu trong các bài tiếp theo.

Công cụ

Một trong những công cụ hiệu quả học Python là dùng gói Anaconda. Một số điều cần lưu ý:

Thuật toán k-Nearest Neighbors (kNN)

Đây là thuật toán phân loại cơ bản nhất trong nhánh Supervise Learning (Học có giám sát) của Machine Learning.

Ý tưởng đơn giản về kNN: Cho bốn điểm M(1.0,1.1), N(1.0,1.0), P(0,0) ,K(0, 0.1). Tìm điểm gần nhất với một điểm O (0, 0) bất kỳ trong số k điểm (chúng ta đang xét k = 4).

Hình ảnh biểu diễn các điểm:

Giải pháp: Tính khoảng cách từ k (Minh họa của chúng ta là k = 4) điểm đến điểm O và so sánh các khoảng cách này.

Tổ chức dữ liệu

Với vấn đề được nêu ở trên, dữ liệu được tổ chức trong một hàm gọi là createDataSet() có đoạn mã như sau:


def createDataSet():

   group = array([[1.0,1.1],[1.0,1.0],[0,0],[0,0.1]])

   labels = ['A','A','B','B']

   return group, labels

Ở đây, tọa độ các điểm được tổ chức thành một ma trận tên group. Các điểm được phân loại trong hai nhóm là A và B.

Một số vấn đề chúng ta cần lưu ý:

  • Cách viết các lệnh
  • Biến trong Python: biến group và labels
  • Định nghĩa hàm trong Python

Cách viết các lệnh

Hàm createDataSet có 3 lệnh: khai báo biến group, khai báo biến labels và lệnh return (các lệnh này sẽ đề cập chi tiết phần dưới). Các ngôn ngữ khác như C, C# hay Java dùng cặp dấu ngoặc nhọn ({}) để đánh dấu lệnh hay khối lệnh con, trong Python dùng dấu thụt dòng hay một ký tự trắng (Indentation). Để ý 3 lệnh trên của createDataSet thụt vào trong so với lệnh định nghĩa hàm createDataSet (sẽ đề cập phần dưới).

Nếu ai có kinh nghiệm với VB.NET thì sẽ thích Python vì các lệnh không cần kết thúc bằng một ký hiệu, ví dụ dấu chấm phẩy như với C, C#,…

Kiểu dữ liệu

Ở đây chúng ta có các giá trị ‘A’ hay ‘B’ là các ký tự. Trong các ngôn ngữ như C, Java, JavaScript, C#, v.v. chuỗi và ký tự có sự phân biệt. Thông thường, chuỗi được đặt trong nháy kép và ký tự trong nháy đơn. Trong Python một chuỗi có thể được đặt trong nháy kép hay nháy đơn. Nói cách khác, có thể hiểu ‘A’ hay ‘B’ là các chuỗi đặc biệt trong Python.

Các giá trị 1.0, 1.1,… có kiểu float.

Một dãy các số hay một dãy các chuỗi có thể được biểu diễn theo kiểu list trong Python. Ví dụ [1.0, 1.1], [1.0,1.0], [0,0], [0,0.1] hay [‘A’,’A’,’B’,’B’] là các list.

Mỗi list chứa giá trị số có thể được gọi là một vectơ. Ví dụ tọa độ các điểm đã cho ở trên có thể biểu diễn thành các vec tơ [1.0, 1.1], [1.0, 1.0], [0,0], [0, 0.1].

Tập hợp các list giá trị số (hay vec tơ) sẽ tạo thành một ma trận, ví dụ ma trận các vec tơ tọa độ

[[1.0, 1.1],

[1.0,1.0],

[0,0],

[0, 0.1] ]

Các biến

Các biến trong Python không cần kiểu dữ liệu và phải được gán với giá trị ngay khi khai báo. Kiểu dữ liệu của biến chính là kiểu dữ liệu của giá trị được gán đến biến đó. Biến group labels là hai ví dụ về biến trong Python.

Định nghĩa hàm

Giống như các ngôn ngữ khác, hàm trong Python có hai kiểu là có giá trị trả về dùng kèm với từ khóa return và không có giá trị trả về hay không có từ khóa return. Và cũng giống như các ngôn ngữ khác, hàm Python có thể có tham số hoặc không có tham số.

Hàm trong Python được định nghĩa bắt đầu bằng từ khóa def. Xem lại định nghĩa hàm createDataSet như sau:


def createDataSet():

  group = array([[1.0,1.1],[1.0,1.0],[0,0],[0,0.1]])

  labels = ['A','A','B','B']

  return group, labels

Chúng ta để ý một số vấn đề sau:

  • Hàm bắt đầu bằng từ khóa def
  • Định nghĩa hàm không cần kèm theo kiểu dữ liệu
  • Tên hàm là createDataSet
  • Hàm không có tham số nhưng phải có cặp ngoặc (giống với hầu hết ngôn ngữ lập trình)
  • Sau cặp ngoặc (chứa hoặc không chứa tham số) là dấu hai chấm
  • Hàm có giá trị trả về với từ khóa return
  • Các lệnh bên trong hàm phải thụt vào một ký tự trắng so với lệnh định nghĩa hàm bắt đầu bằng def

Sử dụng hàm createDataSet

Giả sử chúng ta điều chỉnh lại hàm createDataSet như sau:


def createDataSet():

  group = [[1.0,1.1],[1.0,1.0],[0,0],[0,0.1]]

  labels = ['A','A','B','B']

  return group, labels

Ở đây, khai báo biến group được điều chỉnh lại. Lý do cho việc này chúng ta sẽ hiểu trong các bước kế tiếp.

Hàm createDataSet() trả về một giá trị kiểu ma trận (group) và một giá trị kiểu danh sách (labels). Hai giá trị này có thể được gán đến các biến trong một dòng lệnh như sau:


group, labels = createDataSet()

Đoạn mã trên sử dụng hai biến trùng tên với hai biến cục bộ của hàm createDataSet nhằm nhấn mạnh tính thứ tự tương ứng với các giá trị trả về.

Bây giờ, chúng ta sẽ hiển thị giá trị các biến grouplabels bằng hàm print như sau:


print(group)

print(labels)

Kết quả:

[[1.0, 1.1], [1.0, 1.0], [0, 0], [0, 0.1]]

[‘A’, ‘A’, ‘B’, ‘B’]

Chúng ta thấy rằng, giá trị biến group là một danh sách chứa các danh sách con hay là một ma trận. Cách biểu diễn này khá đẹp mắt nhưng sẽ khó tính toán về sau. Để tiện tính toán, chúng ta sẽ dùng phương thức array trong khai báo biến group trong hàm createDataSet như sau:


group = array([[1.0,1.1],[1.0,1.0],[0,0],[0,0.1]])

Thực thi lại và xem kết quả:

[[ 1. 1.1]

[ 1. 1. ]

[ 0. 0. ]

[ 0. 0.1]]

[‘A’, ‘A’, ‘B’, ‘B’]

Biến group bây giờ có thể hiểu là một ma trận gồm hai cột với cột đầu tiên có chỉ số 0, cột thứ hai chỉ số 1, và cứ thế tiếp tục. Hiểu điều này là quan trọng để sử dụng biến group sau này. Ví dụ, xem giá trị cột thứ nhất của ma trận group, chúng ta dùng lệnh:


print(group[:,0])

Kết quả:

[ 1. 1. 0. 0.]

Tương tự, xem giá trị cột thứ 2:


print(group[:,1])

Tính khoảng cách giữa các điểm

Vì trong phần này chúng ta sẽ thực hiện khá nhiều các thao tác, các phép toán toán học nên chúng ta cần sử dụng các mô đun. Các mô đun là các thư viện chứa các phương thức hay hàm được xây dựng sẵn giúp viết chương trình nhanh chóng hơn. Mô đun phổ biến dùng trong Python là NumPy


from numpy import *

import operator

NumPy quá quan trọng nên chúng ta muốn dùng tất cả mọi thứ từ mô đun này bằng cách dùng ký hiệu *. Chúng ta cũng cần dùng một vài thao tác toán học khác từ mô đun Operator.

Khoảng cách từ A (xA,yA) đến B (xB, yB) được tính theo công thức sau:

Như vậy, khoảng cách từ 4 điểm M, N, P, K đến O được tính như sau:

Chúng ta đã tổ chức tọa độ các điểm M, N, P, K trong ma trận group như sau:

Để tính khoảng cách từ O ( hay một điểm bất kỳ) đến M, N, P, K thì ta sẽ biến tọa độ điểm O thành ma trận như sau:

Ma trận trên là ma trận cùng cấp với group và mỗi hàng là vec tơ tọa độ của O. Hay nói cách khác chúng ta nhân bản vec tơ tọa độ điểm O lên 4 lần theo hàng. Chúng ta có thể thực hiện dễ dàng với điều này dùng Python dùng hàm tile:


tile([0,0],(4,1))

Xem thêm về cách dùng tile tại https://docs.scipy.org/doc/numpy/reference/generated/numpy.tile.html

Chúng ta có thể tính hiệu các tọa độ như sau:

Bước kế tiếp là chúng ta bình phương tất cả các phần tử của ma trận hiệu vừa đạt được bằng cách bình phương ma trận Hieu. Trong Python chúng ta có thể bình phương ma trận như sau:

Tiếp theo chúng ta tính tổng các bình phương từ ma trận Binhphuong theo cột bằng cách dùng hàm sum như sau:


Khoangcachbp = Binhphuong.sum(axis = 1)

Tham số axis của hàm sum bằng 0 hay 1 phụ thuộc vào chúng ta muốn tính tổng các phần tử trong một ma trận theo hàng hay cột. Với đoạn mã như trên, Khoangcachbp sẽ là một vec tơ như sau:

Cuối cùng chúng ta sẽ có được vec tơ Khoangcach chứa các khoảng cách OM, ON, OP, OK bằng cách lấy căn bậc hai Khoangcachbp

(Còn nữa)