SGDClassifier: Học trực tuyến / part_fit với nhãn chưa biết trước đó


9

Tập huấn luyện của tôi chứa khoảng 50k mục mà tôi học lần đầu. Trên cơ sở hàng tuần, ~ 5k mục được thêm vào; nhưng cùng một lượng "biến mất" (vì đó là dữ liệu người dùng phải xóa sau một thời gian).

Do đó, tôi sử dụng học trực tuyến vì sau này tôi không có quyền truy cập vào bộ dữ liệu đầy đủ. Hiện tại tôi đang sử dụng một SGDClassifiercông cụ hoạt động, nhưng vấn đề lớn của tôi: các danh mục mới đang xuất hiện và bây giờ tôi không thể sử dụng mô hình của mình nữa vì chúng không có trong phần đầu fit.

Có cách nào với SGDClassifierhoặc một số mô hình khác? Học kĩ càng?

Sẽ không có vấn đề gì nếu tôi phải bắt đầu lại từ đầu NGAY BÂY GIỜ (tức là sử dụng thứ gì đó khác SGDClassifier), nhưng tôi cần thứ gì đó cho phép học trực tuyến với nhãn mới.


1
Khi bạn nói rằng bạn có các danh mục mới, bạn đang nói về các danh mục mới trong các biến ngoại sinh ( ) hoặc trong các biến nội sinh ( X ) của bạn? YX
Juan Esteban de la Calle

Câu trả lời:


9

Có vẻ như bạn không muốn bắt đầu đào tạo lại mô hình mỗi khi danh mục nhãn mới xuất hiện. Các đơn giản nhất cách để lưu giữ thông tin tối đa của dữ liệu quá khứ sẽ là đào tạo một phân loại cho mỗi thể loại.

Bằng cách này, bạn có thể tiếp tục huấn luyện từng bộ phân loại tăng dần ("trực tuyến") với một cái gì đó giống như SGDClassifierkhông cần phải đào tạo lại chúng. Bất cứ khi nào một danh mục mới xuất hiện, bạn thêm một trình phân loại nhị phân mới cho danh mục đó. Sau đó, bạn chọn lớp có xác suất / điểm cao nhất trong số các bộ phân loại.

Điều này cũng không khác nhiều so với những gì bạn đang làm ngày hôm nay, bởi vì scikit's SDGClassifierđã xử lý kịch bản đa giác bằng cách lắp nhiều phân loại "Một so với tất cả" dưới mui xe.

Tất nhiên, nếu nhiều danh mục mới tiếp tục được đưa ra, cách tiếp cận này có thể trở nên hơi khó quản lý.


1
Tài giỏi! Phương pháp này cũng có thể hoạt động tốt với các trình phân loại scikit khác có warm_starttùy chọn.
Simon Larsson

5

Nếu các danh mục mới rất hiếm khi xuất hiện, bản thân tôi thích giải pháp "một so với tất cả" được cung cấp bởi @oW_ . Đối với mỗi danh mục mới, bạn huấn luyện một mô hình mới về số lượng mẫu X từ danh mục mới (loại 1) và số lượng mẫu X từ các loại còn lại (loại 0).

Tuy nhiên, nếu các danh mục mới đến thường xuyên và bạn muốn sử dụng một mô hình được chia sẻ duy nhất , có một cách để thực hiện điều này bằng cách sử dụng các mạng thần kinh.

Tóm lại, khi xuất hiện một danh mục mới, chúng tôi thêm một nút mới tương ứng vào lớp softmax với trọng số bằng không (hoặc ngẫu nhiên) và giữ nguyên trọng số cũ, sau đó chúng tôi huấn luyện mô hình mở rộng với dữ liệu mới. Dưới đây là một bản phác thảo trực quan cho ý tưởng (được vẽ bởi chính tôi):

Đây là một triển khai cho kịch bản hoàn chỉnh:

  1. Mô hình được đào tạo trên hai loại,

  2. Một danh mục mới đến,

  3. Các định dạng mô hình và đích được cập nhật tương ứng,

  4. Mô hình được đào tạo về dữ liệu mới.

Mã số:

from keras import Model
from keras.models import Sequential
from keras.layers import Dense
from keras.optimizers import Adam
from sklearn.metrics import f1_score
import numpy as np


# Add a new node to the last place in Softmax layer
def add_category(model, pre_soft_layer, soft_layer, new_layer_name, random_seed=None):
    weights = model.get_layer(soft_layer).get_weights()
    category_count = len(weights)
    # set 0 weight and negative bias for new category
    # to let softmax output a low value for new category before any training
    # kernel (old + new)
    weights[0] = np.concatenate((weights[0], np.zeros((weights[0].shape[0], 1))), axis=1)
    # bias (old + new)
    weights[1] = np.concatenate((weights[1], [-1]), axis=0)
    # New softmax layer
    softmax_input = model.get_layer(pre_soft_layer).output
    sotfmax = Dense(category_count + 1, activation='softmax', name=new_layer_name)(softmax_input)
    model = Model(inputs=model.input, outputs=sotfmax)
    # Set the weights for the new softmax layer
    model.get_layer(new_layer_name).set_weights(weights)
    return model


# Generate data for the given category sizes and centers
def generate_data(sizes, centers, label_noise=0.01):
    Xs = []
    Ys = []
    category_count = len(sizes)
    indices = range(0, category_count)
    for category_index, size, center in zip(indices, sizes, centers):
        X = np.random.multivariate_normal(center, np.identity(len(center)), size)
        # Smooth [1.0, 0.0, 0.0] to [0.99, 0.005, 0.005]
        y = np.full((size, category_count), fill_value=label_noise/(category_count - 1))
        y[:, category_index] = 1 - label_noise
        Xs.append(X)
        Ys.append(y)
    Xs = np.vstack(Xs)
    Ys = np.vstack(Ys)
    # shuffle data points
    p = np.random.permutation(len(Xs))
    Xs = Xs[p]
    Ys = Ys[p]
    return Xs, Ys


def f1(model, X, y):
    y_true = y.argmax(1)
    y_pred = model.predict(X).argmax(1)
    return f1_score(y_true, y_pred, average='micro')


seed = 12345
verbose = 0
np.random.seed(seed)

model = Sequential()
model.add(Dense(5, input_shape=(2,), activation='tanh', name='pre_soft_layer'))
model.add(Dense(2, input_shape=(2,), activation='softmax', name='soft_layer'))
model.compile(loss='categorical_crossentropy', optimizer=Adam())

# In 2D feature space,
# first category is clustered around (-2, 0),
# second category around (0, 2), and third category around (2, 0)
X, y = generate_data([1000, 1000], [[-2, 0], [0, 2]])
print('y shape:', y.shape)

# Train the model
model.fit(X, y, epochs=10, verbose=verbose)

# Test the model
X_test, y_test = generate_data([200, 200], [[-2, 0], [0, 2]])
print('model f1 on 2 categories:', f1(model, X_test, y_test))

# New (third) category arrives
X, y = generate_data([1000, 1000, 1000], [[-2, 0], [0, 2], [2, 0]])
print('y shape:', y.shape)

# Extend the softmax layer to accommodate the new category
model = add_category(model, 'pre_soft_layer', 'soft_layer', new_layer_name='soft_layer2')
model.compile(loss='categorical_crossentropy', optimizer=Adam())

# Test the extended model before training
X_test, y_test = generate_data([200, 200, 0], [[-2, 0], [0, 2], [2, 0]])
print('extended model f1 on 2 categories before training:', f1(model, X_test, y_test))

# Train the extended model
model.fit(X, y, epochs=10, verbose=verbose)

# Test the extended model on old and new categories separately
X_old, y_old = generate_data([200, 200, 0], [[-2, 0], [0, 2], [2, 0]])
X_new, y_new = generate_data([0, 0, 200], [[-2, 0], [0, 2], [2, 0]])
print('extended model f1 on two (old) categories:', f1(model, X_old, y_old))
print('extended model f1 on new category:', f1(model, X_new, y_new))

đầu ra nào:

y shape: (2000, 2)
model f1 on 2 categories: 0.9275
y shape: (3000, 3)
extended model f1 on 2 categories before training: 0.8925
extended model f1 on two (old) categories: 0.88
extended model f1 on new category: 0.91

Tôi nên giải thích hai điểm liên quan đến đầu ra này:

  1. Hiệu suất mô hình bị giảm từ 0.9275đến 0.8925chỉ bằng cách thêm một nút mới. Điều này là do đầu ra của nút mới cũng được đưa vào để lựa chọn danh mục. Trong thực tế, đầu ra của nút mới chỉ được đưa vào sau khi mô hình được đào tạo trên một mẫu khá lớn. Ví dụ, chúng ta nên đạt đỉnh lớn nhất trong hai mục đầu tiên [0.15, 0.30, 0.55], tức là lớp 2, ở giai đoạn này.

  2. Hiệu suất của mô hình mở rộng trên hai loại (cũ) 0.88ít hơn mô hình cũ 0.9275. Điều này là bình thường, bởi vì bây giờ mô hình mở rộng muốn gán đầu vào cho một trong ba loại thay vì hai. Mức giảm này cũng được dự kiến ​​khi chúng ta chọn từ ba phân loại nhị phân so với hai phân loại nhị phân theo cách tiếp cận "một so với tất cả".


1

Tôi phải nói rằng tôi chưa tìm thấy tài liệu nào liên quan đến chủ đề này. Theo tôi biết, những gì bạn yêu cầu là không thể. Bạn nên lưu ý về điều này, và chủ sở hữu sản phẩm cũng vậy. Lý do là bất kỳ chức năng mất nào đều phụ thuộc vào các nhãn đã biết, vì vậy không có cách nào bạn có thể dự đoán một nhãn không có trong dữ liệu huấn luyện. Ngoài ra, khoa học viễn tưởng cho rằng thuật toán học máy có thể dự đoán thứ gì đó mà nó chưa được đào tạo

Đã nói như vậy, tôi nghĩ rằng có thể có một cách giải quyết (để tôi chỉ ra rằng đây là một ý kiến ​​không dựa trên văn học chính thức). Nếu bộ phân loại có xác suất, thì đầu ra là xác suất cho mỗi lớp là đúng và phân rã là đầu dò cao hơn. Có lẽ bạn có thể đặt ngưỡng cho xác suất đó, sao cho mô hình dự đoán "không xác định" nếu tất cả các xác suất đều nằm dưới ngưỡng đó. Tôi sẽ cho bạn một ví dụ.

M(x)xxc1,c2,c3MppM(x)=p(x)=(0.2,0.76,0.5)xc2τpiτx

Bạn làm gì với những điều chưa biết đó phụ thuộc vào logic của bussines. Nếu chúng quan trọng, bạn có thể tạo một nhóm chúng và đào tạo lại mô hình bằng cách sử dụng dữ liệu có sẵn. Tôi nghĩ bạn có thể thực hiện "chuyển giao học tập" từ mô hình được đào tạo bằng cách thay đổi kích thước của đầu ra. Nhưng đây là điều tôi chưa từng đối mặt, vì vậy tôi chỉ nói

Hãy tính số lượng SGDClassifiersử dụng SVMbên dưới, đây không phải là một thuật toán xác suất. Theo SGDClassifiertài liệu sau, bạn có thể sửa đổi lossđối số thành modified_huberhoặc logđể có được kết quả xác suất.


0

Có hai lựa chọn:

  1. Dự đoán cơ hội của một datapoint thuộc về một ẩn số hoặc unkdanh mục. Bất kỳ danh mục mới nào xuất hiện trong luồng nên được dự đoán là unk. Điều này là phổ biến trong Xử lý ngôn ngữ tự nhiên (NLP) vì luôn có các mã thông báo từ mới xuất hiện trong các luồng từ.

  2. Đào tạo lại mô hình mỗi khi một danh mục mới xuất hiện.

Vì bạn đề cập SGDClassifier, tôi giả sử bạn sử dụng scikit-learn. Scikit-learn không hỗ trợ học trực tuyến rất tốt. Sẽ tốt hơn nếu chuyển đổi một khung hỗ trợ tốt hơn cho việc phát trực tuyến và học trực tuyến, chẳng hạn như Spark .

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.