SGDClassifier: एक पूर्व अज्ञात लेबल के साथ ऑनलाइन लर्निंग / आंशिक_फिट


9

मेरे प्रशिक्षण सेट में लगभग 50k प्रविष्टियाँ हैं जिनके साथ मैं एक प्रारंभिक शिक्षा करता हूँ। साप्ताहिक आधार पर, ~ 5k प्रविष्टियाँ जोड़ी जाती हैं; लेकिन एक ही राशि "गायब हो जाती है" (जैसा कि यह उपयोगकर्ता डेटा है जिसे कुछ समय बाद हटाना होगा)।

इसलिए मैं ऑनलाइन सीखने का उपयोग करता हूं क्योंकि मेरे पास बाद में पूर्ण डेटासेट तक पहुंच नहीं है। वर्तमान में मैं एक SGDClassifierकाम का उपयोग कर रहा हूं , लेकिन मेरी बड़ी समस्या: नई श्रेणियां दिखाई दे रही हैं और अब मैं अपने मॉडल का उपयोग नहीं कर सकता क्योंकि वे प्रारंभिक में नहीं थे fit

क्या कोई रास्ता है SGDClassifierया कोई अन्य मॉडल है? ध्यान लगा के पढ़ना या सीखना?

इससे कोई फर्क नहीं पड़ता कि मुझे स्क्रैच नाउ से शुरू करना है (यानी इसके अलावा कुछ का उपयोग करें SGDClassifier), लेकिन मुझे कुछ ऐसा चाहिए जो नए लेबल के साथ ऑनलाइन सीखने में सक्षम हो।


1
जब आप कहते हैं कि आपके पास नई श्रेणियां हैं, तो क्या आप अपने बहिर्जात चर में नई श्रेणियों के बारे में बात कर रहे हैं (Y) या आपके अंतर्जात चर में (X)?
जुआन एस्टेबन डे ला कैले

जवाबों:


9

ऐसा लगता है कि आप हर बार एक नया लेबल श्रेणी प्रकट होने पर मॉडल को फिर से शुरू करना नहीं चाहते हैं। पिछले डेटा की अधिकतम जानकारी को बनाए रखने का सबसे आसान तरीका प्रति वर्ग एक क्लासिफायर ट्रेन होगा।

इस तरह से आप प्रत्येक क्लासिफायरफ़ायर को लगातार ("ऑनलाइन") के साथ प्रशिक्षित कर सकते हैं, जैसे SGDClassifierकि उन्हें वापस लिए बिना। जब भी कोई नई श्रेणी दिखाई देती है तो आप उस श्रेणी के लिए एक नया बाइनरी क्लासिफ़ायर जोड़ते हैं। आप तब कक्षा के सेट के बीच उच्चतम संभावना / स्कोर के साथ वर्ग का चयन करते हैं।

यह भी आप आज जो कुछ कर रहे हैं, उससे बहुत अलग नहीं है, क्योंकि scikit's SDGClassifierपहले से ही हुड के नीचे कई "वन बनाम ऑल" क्लासिफायर फिटिंग करके मल्टीस्कल्स-परिदृश्य को संभालता है।

यदि बहुत सारी नई श्रेणियां सामने आती रहती हैं, तो निश्चित रूप से, यह दृष्टिकोण प्रबंधन करने के लिए थोड़ा मुश्किल हो सकता है।


1
चतुर! यह तरीका अन्य स्किकिट क्लासिफायर के साथ भी अच्छा काम कर सकता है, जिसका warm_startविकल्प है।
शमौन लार्सन

5

यदि नई श्रेणियां बहुत कम पहुंच रही हैं, तो मैं खुद @oW_ द्वारा प्रदान किए गए "एक बनाम सभी" समाधान पसंद करता हूं । प्रत्येक नई श्रेणी के लिए, आप नई श्रेणी (वर्ग 1) से X संख्या के नमूनों पर एक नया मॉडल और शेष श्रेणियों (वर्ग 0) से नमूनों की X संख्या पर प्रशिक्षण देते हैं।

हालाँकि, यदि नई श्रेणियां बार - बार आ रही हैं और आप एकल साझा मॉडल का उपयोग करना चाहते हैं , तो तंत्रिका नेटवर्क का उपयोग करके इसे पूरा करने का एक तरीका है।

सारांश में, एक नई श्रेणी के आगमन पर, हम शून्य (या यादृच्छिक) भार के साथ सॉफ्टमैक्स परत में एक समान नया नोड जोड़ते हैं, और पुराने वजन को बरकरार रखते हैं, फिर हम नए डेटा के साथ विस्तारित मॉडल को प्रशिक्षित करते हैं। इस विचार के लिए एक दृश्य स्केच है (स्वयं द्वारा खींचा गया है):

यहाँ पूर्ण परिदृश्य के लिए एक कार्यान्वयन है:

  1. मॉडल को दो श्रेणियों में प्रशिक्षित किया जाता है,

  2. एक नई श्रेणी आती है,

  3. मॉडल और लक्ष्य प्रारूप तदनुसार अपडेट किए जाते हैं,

  4. मॉडल को नए डेटा पर प्रशिक्षित किया जाता है।

कोड:

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))

कौन से आउटपुट:

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

मुझे इस आउटपुट के बारे में दो बिंदुओं को समझाना चाहिए:

  1. केवल एक नया नोड जोड़ने से मॉडल प्रदर्शन कम हो 0.9275जाता 0.8925है। ऐसा इसलिए है क्योंकि श्रेणी चयन के लिए नए नोड का आउटपुट भी शामिल है। व्यवहार में, नए नोड के आउटपुट को तब ही शामिल किया जाना चाहिए जब मॉडल एक बड़े आकार के नमूने पर प्रशिक्षित किया जाता है। उदाहरण के लिए, हमें [0.15, 0.30, 0.55]इस स्तर पर पहली दो प्रविष्टियों में से सबसे बड़ी यानी दूसरी श्रेणी में चोटी रखनी चाहिए ।

  2. दो (पुरानी) श्रेणियों पर विस्तारित मॉडल का प्रदर्शन 0.88पुराने मॉडल की तुलना में कम है 0.9275। यह सामान्य है, क्योंकि अब विस्तारित मॉडल दो के बजाय तीन श्रेणियों में से एक को इनपुट असाइन करना चाहता है। यह कमी तब भी होती है जब हम "एक बनाम सभी" दृष्टिकोण में दो बाइनरी क्लासिफायर की तुलना में तीन बाइनरी क्लासिफायर से चुनते हैं।


1

मुझे लगता है कि मुझे इस विषय के बारे में कोई साहित्य नहीं मिला है। जहां तक ​​मुझे पता है, आप जो पूछते हैं वह असंभव है। आपको इसके बारे में पता होना चाहिए, और उत्पाद का मालिक भी होना चाहिए। कारण यह है कि कोई भी नुकसान फ़ंक्शन ज्ञात लेबल पर निर्भर करता है, इसलिए ऐसा कोई तरीका नहीं है जिससे आप किसी ऐसे लेबल की भविष्यवाणी कर सकें जो प्रशिक्षण डेटा में नहीं है। इसके अलावा, विज्ञान-कथा है कि एक मशीन लर्निंग एल्गोरिदम कुछ भविष्यवाणी कर सकता है जिसके लिए इसे प्रशिक्षित नहीं किया गया है

ऐसा कहने के बाद, मुझे लगता है कि एक वर्कअराउंड हो सकता है (मुझे बताएं कि यह औपचारिक साहित्य पर आधारित नहीं है)। यदि क्लासिफायर संभाव्य है, तो आउटपुट प्रत्येक वर्ग के लिए सही होने की संभावना है और डिक्शन का अधिक होना। हो सकता है कि आप उस संभावना के लिए एक सीमा निर्धारित कर सकें, जैसे कि मॉडल "अज्ञात" की भविष्यवाणी करता है यदि सभी संभावनाएं उस सीमा से नीचे हैं। मैं आपको एक उदाहरण देता हूं।

चलो M(x) एक मॉडल हो जैसे कि: a xतय करता है कि क्या x तीन श्रेणियों में से एक से संबंधित है c1,c2,c3। का उत्पादनM संभावनाओं का एक सदिश है p। निर्णय उच्चतम संभावना को ध्यान में रखकर किया जाता हैp। तो का एक आउटपुटM(x)=p(x)=(0.2,0.76,0.5) निर्णय के अनुरूप होगा x का है c2। आप एक सेट करके इस निर्णय को संशोधित कर सकते हैंτ अगर कोई नहीं है piτ फिर निर्णय है x अज्ञात वर्ग का है

आप उन अनजान लोगों के साथ क्या करते हैं यह बूसी तर्क पर निर्भर करता है। यदि वे महत्वपूर्ण हैं, तो आप उनमें से एक पूल बना सकते हैं और उपलब्ध डेटा का उपयोग करके मॉडल को फिर से प्रशिक्षित कर सकते हैं। मुझे लगता है कि आप आउटपुट के आयाम को बदलकर प्रशिक्षित मॉडल से "ट्रांसफर लर्निंग" कर सकते हैं। लेकिन यह कुछ ऐसा है जिसका मैंने सामना नहीं किया है, इसलिए मैं सिर्फ कह रहा हूं

नीचे की ओर SGDClassifierउपयोग करने वाली गिनती पर ध्यान दें SVM, जो कि एक संभाव्य एल्गोरिथ्म नहीं है। SGDClassifierदस्तावेज़ीकरण के बाद आप lossतर्क को संशोधित कर सकते हैं modified_huberया logसंभाव्य आउटपुट प्राप्त करने के लिए।


0

दो विकल्प हैं:

  1. किसी अज्ञात या unkश्रेणी से संबंधित डेटापॉइंट का मौका भविष्यवाणी करें । धारा में दिखाई देने वाली किसी भी नई श्रेणियों की भविष्यवाणी की जानी चाहिए unk। यह प्राकृतिक भाषा प्रसंस्करण (एनएलपी) में आम है क्योंकि शब्द धाराओं में हमेशा नए शब्द टोकन दिखाई देते हैं।

  2. हर बार एक नई श्रेणी दिखाई देने पर मॉडल को फिर से लिखें।

चूंकि आप उल्लेख करते हैं SGDClassifier, इसलिए मैं आपको scikit-learn का उपयोग करते हुए मानता हूं। स्किकिट-लर्न ऑनलाइन लर्निंग का बहुत अच्छा समर्थन नहीं करता है। एक फ्रेमवर्क को स्विच करना बेहतर होगा जो स्पार्क जैसे स्ट्रीमिंग और ऑनलाइन सीखने का समर्थन करता है ।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.