Đây là một vấn đề hấp dẫn! Hai điều làm cho nó đặc biệt thách thức:
- Làm thế nào chúng ta nên so sánh hai bộ điểm? Các vấn đề kinh điển trong Machine Learning có số lượng thuộc tính cố định và các thuộc tính này không thể thay thế cho nhau: Ví dụ: tôi có thể có dữ liệu về những người khác nhau có thuộc tính
age
và height
(tính bằng centimet). Mỗi mẫu có một mục nhập cho mỗi mục, và tất nhiên (age, height) = (22, 180)
không giống như (age, height) = (180, 22)
. Không phải là sự thật trong vấn đề của bạn. Một tập hợp điểm có từ 3 đến 10 điểm và thứ tự chúng tôi nhập vào các điểm sẽ không tạo ra sự khác biệt khi so sánh hai tập hợp điểm.
- Làm thế nào để chúng ta đưa ra một dự đoán? Giả sử chúng tôi đã tìm ra cách chọn tập hợp điểm từ tập huấn luyện của chúng tôi tương tự như tập điểm của bạn ở trên. Chúng tôi phải đối mặt với vấn đề rằng dự đoán của chúng tôi phải là một trong 7 điểm trong bức tranh của bạn; nhưng không có điểm nào trong số này có thể được chứa trong các tập hợp điểm tương tự.
Hãy để tôi phác thảo một thuật toán liên quan đến cả hai thách thức. Độ chính xác dự đoán không tốt lắm; nhưng có lẽ bạn thấy một cách làm thế nào nó có thể được cải thiện. Và ít nhất nó dự đoán một cái gì đó , phải không?
1. Mô phỏng mẫu
Để có thể kiểm tra thuật toán, tôi đã viết các hàm tạo mẫu và nhãn.
Tạo mẫu:
Mỗi mẫu chứa từ 3 đến 10 điểm. Số lượng điểm là ngẫu nhiên, được rút ra từ một phân phối thống nhất. Mỗi điểm là của hình thức (x_coordinate, y_coordinate)
. Các tọa độ một lần nữa là ngẫu nhiên, được rút ra từ một phân phối bình thường.
import numpy as np
from random import randint
def create_samples(number_samples, min_points, max_points):
def create_single_sample(min_points, max_points):
n = randint(min_points, max_points)
return np.array([np.random.normal(size=2) for _ in range(n)])
return np.array([create_single_sample(min_points, max_points) for _ in range(number_samples)])
Tạo nhãn: Như một ví dụ về đồ chơi, chúng ta hãy giả sử rằng quy tắc chọn điểm là: Luôn chọn điểm gần nhất (0, 0)
, trong đó 'gần nhất' nên được hiểu theo tiêu chuẩn Euclide.
def decision_function_minnorm(sample):
norms = np.apply_along_axis(np.linalg.norm, axis=1, arr=sample)
return sample[norms.argmin()]
def create_labels(samples, decision_function):
return np.array([decision_function(sample) for sample in samples])
Bây giờ chúng ta có thể tạo các bộ thử nghiệm và xe lửa của mình:
n_train, n_test = 1000, 100
dec_fun = decision_function_minnorm
X_train = create_samples(number_samples=n_train, min_points=3, max_points=10)
X_test = create_samples(number_samples=n_test, min_points=3, max_points=10)
y_train = create_labels(X_train, dec_fun)
y_test = create_labels(X_test, dec_fun)
2. So sánh các tập hợp điểm qua khoảng cách Hausdorff
Hãy để chúng tôi giải quyết vấn đề đầu tiên: Làm thế nào chúng ta nên so sánh các tập hợp điểm khác nhau? Số lượng điểm trong bộ điểm là khác nhau. Cũng nên nhớ rằng thứ tự chúng tôi ghi lại các điểm không quan trọng: So sánh với tập hợp điểm [(0,0), (1,1), (2,2)]
sẽ mang lại kết quả tương tự như so với tập hợp điểm [(2,2), (0,0), (1,1)]
. Cách tiếp cận của tôi là so sánh các tập hợp điểm thông qua khoảng cách Hausdorff của chúng :
def hausdorff(A, B):
def dist_point_to_set(x, A):
return min(np.linalg.norm(x - a) for a in A)
def dist_set_to_set(A, B):
return max(dist_point_set(a, B) for a in A)
return max(dist_set_to_set(A, B), dist_set_to_set(B, A))
3. Dự đoán qua hàng xóm k gần nhất và tính trung bình
Bây giờ chúng ta có một khái niệm về khoảng cách giữa các bộ điểm. Điều này cho phép sử dụng phân loại hàng xóm gần nhất k: Với tập hợp điểm kiểm tra, chúng tôi tìm thấy các k
tập hợp điểm trong mẫu đào tạo của chúng tôi có khoảng cách Hausdorff nhỏ nhất so với tập hợp điểm kiểm tra và lấy nhãn của chúng. Bây giờ đến vấn đề thứ hai: Làm thế nào để chúng ta biến các k
nhãn này thành một dự đoán cho tập hợp điểm kiểm tra? Tôi đã sử dụng cách tiếp cận đơn giản nhất: lấy trung bình các nhãn và dự đoán điểm trong tập hợp điểm kiểm tra gần nhất với mức trung bình.
def predict(x, num_neighbors):
# Find num_neighbors closest points in X_train.
distances_to_train = np.array([hausdorff(x, x_train) for x_train in X_train])
neighbors_idx = np.argpartition(distances_to_train, -num_neighbors)[-num_neighbors:]
# Get labels of the neighbors and calculate the average.
targets_neighbors = y_train[neighbors_idx]
targets_mean = sum(targets_neighbors) / num_neighbors
# Find point in x that is closest to targets_mean and use it as prediction.
distances_to_mean = np.array([np.linalg.norm(p - targets_mean) for p in x])
closest_point = x[distances_to_mean.argmin()]
return closest_point
4. Kiểm tra
Tất cả mọi thứ là ở chỗ để kiểm tra hiệu suất của thuật toán của chúng tôi.
num_neighbors = 70
successes = 0
for i, x in enumerate(X_test):
print('%d/%d' % (i+1, n_test))
prediction = predict(x, num_neighbors)
successes += np.array_equal(prediction, y_test[i])
Đối với hàm quyết định đã cho và num_neighbors = 70
, chúng tôi có độ chính xác dự đoán là 84%. Điều này không phải là quá tốt, và tất nhiên nó là cụ thể cho chức năng quyết định của chúng tôi, điều này có vẻ khá dễ dự đoán.
Để thấy điều này, hãy xác định một hàm quyết định khác:
decision_function_maxaverage(sample):
avgs = (sample[:, 0] + sample[:, 1]) / 2
return sample[norms.argmin()]
Sử dụng chức năng này thông qua dec_fun = decision_function_maxaverage
giảm độ chính xác dự đoán xuống 45%. Điều này cho thấy tầm quan trọng của việc suy nghĩ về các quy tắc quyết định tạo nhãn của bạn. Nếu bạn có một ý tưởng tại sao mọi người chọn một số điểm nhất định, điều này sẽ giúp bạn tìm ra thuật toán tốt nhất.
Một số cách để cải thiện thuật toán này: (1) Sử dụng hàm khoảng cách khác thay vì khoảng cách Hausdorff, (2) sử dụng thứ gì đó tinh vi hơn so với hàng xóm gần nhất k, (3) cải thiện cách các nhãn đào tạo được chọn được biến thành dự đoán.