SGDClassifier: การเรียนรู้ออนไลน์ / partial_fit ที่มีป้ายกำกับที่ไม่รู้จักก่อนหน้านี้


9

ชุดการฝึกอบรมของฉันมีรายการประมาณ 50k ซึ่งฉันได้เรียนรู้เบื้องต้น ทุกสัปดาห์จะมีการเพิ่ม ~ 5k รายการ แต่จำนวนเดียวกัน "หายไป" (เนื่องจากเป็นข้อมูลผู้ใช้ซึ่งจะต้องถูกลบหลังจากเวลาผ่านไป)

ดังนั้นฉันใช้การเรียนรู้ออนไลน์เพราะฉันไม่สามารถเข้าถึงชุดข้อมูลแบบเต็มได้ในภายหลัง ขณะนี้ฉันกำลังใช้SGDClassifierที่ทำงาน แต่ปัญหาใหญ่ของฉัน: หมวดหมู่ใหม่จะปรากฏและตอนนี้ฉันไม่สามารถใช้รูปแบบของฉันใด ๆ fitเพิ่มเติมขณะที่พวกเขาไม่ได้อยู่ในการเริ่มต้น

มีวิธีการกับSGDClassifierหรือบางรุ่นอื่น ๆ ? เรียนรู้อย่างลึกซึ้ง?

ไม่สำคัญว่าฉันจะต้องเริ่มจากศูนย์ตอนนี้ (เช่นใช้อย่างอื่นที่ไม่ใช่SGDClassifier) แต่ฉันต้องการสิ่งที่ช่วยให้การเรียนรู้ออนไลน์ด้วยป้ายกำกับใหม่


1
เมื่อคุณพูดว่าคุณมีหมวดหมู่ใหม่คุณกำลังพูดถึงหมวดหมู่ใหม่ในตัวแปรภายนอกของคุณ (Y) หรือในตัวแปรภายนอกของคุณ (X)?
Juan Esteban de la Calle

คำตอบ:


9

ดูเหมือนว่าคุณไม่ต้องการเริ่มการฝึกอบรมซ้ำทุกครั้งที่มีหมวดหมู่ป้ายกำกับใหม่ปรากฏขึ้น ง่ายที่สุดวิธีการเก็บข้อมูลสูงสุดของข้อมูลที่ผ่านมาจะเป็นรถไฟลักษณนามต่อหมวดหมู่

วิธีนี้คุณสามารถฝึกอบรมตัวจําแนกแต่ละคนทีละน้อย ("ออนไลน์") กับสิ่งที่ชอบSGDClassifierโดยไม่ต้องฝึกฝนใหม่ เมื่อใดก็ตามที่หมวดหมู่ใหม่ปรากฏขึ้นคุณจะเพิ่มตัวจําแนกไบนารีใหม่สําหรับหมวดหมู่นั้น จากนั้นคุณเลือกชั้นเรียนที่มีความน่าจะเป็น / คะแนนสูงสุดระหว่างชุดตัวแยกประเภท

สิ่งนี้ก็ไม่ได้แตกต่างจากสิ่งที่คุณทำในวันนี้มากนักเนื่องจากscikit's SDGClassifierจัดการกับสถานการณ์แบบหลายคลาสโดยปรับตัวแยกประเภท "One vs All" หลายตัวไว้ใต้ฝากระโปรง

หากมีหมวดหมู่ใหม่ ๆ เกิดขึ้นมากมายแน่นอนว่าวิธีการนี้อาจกลายเป็นเรื่องยุ่งยากเล็กน้อยในการจัดการ


1
ฉลาด! วิธีนี้อาจทำงานได้ดีกับตัวแยกประเภท scikit อื่น ๆ ที่มีwarm_startตัวเลือก
Simon Larsson

5

ถ้าหมวดหมู่ใหม่จะมาถึงน้อยมากตัวผมเองชอบ "หนึ่งเทียบกับทุกคน" วิธีการแก้ปัญหาให้โดย@oW_ สำหรับแต่ละหมวดหมู่ใหม่คุณจะฝึกโมเดลใหม่ด้วยจำนวนตัวอย่าง X จากหมวดหมู่ใหม่ (คลาส 1) และจำนวนตัวอย่าง X จากหมวดที่เหลือ (คลาส 0)

อย่างไรก็ตามหากหมวดหมู่ใหม่มาถึงบ่อยครั้งและคุณต้องการใช้รูปแบบที่ใช้ร่วมกันเพียงครั้งเดียวก็มีวิธีที่จะทำให้สำเร็จได้โดยใช้เครือข่ายประสาท

โดยสรุปเมื่อการมาถึงของหมวดหมู่ใหม่เราเพิ่มโหนดใหม่ที่สอดคล้องกับเลเยอร์ softmax ที่มีน้ำหนักเป็นศูนย์ (หรือสุ่ม) และรักษาน้ำหนักเก่าไว้เหมือนเดิมจากนั้นเราจะทำการฝึกอบรมโมเดลเพิ่มเติมด้วยข้อมูลใหม่ นี่คือร่างภาพสำหรับแนวคิด (วาดด้วยตัวเอง):

นี่คือการดำเนินการสำหรับสถานการณ์ที่สมบูรณ์:

  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 ในขั้นตอนนี้

  2. ผลการดำเนินงานของรูปแบบการขยายสอง (เดิม) ประเภทน้อยกว่ารุ่นเก่า0.88 0.9275นี่เป็นเรื่องปกติเพราะตอนนี้โมเดลเพิ่มเติมต้องการกำหนดอินพุตให้กับหนึ่งในสามหมวดหมู่แทนที่จะเป็นสองประเภท การลดลงนี้ก็คาดว่าเมื่อเราเลือกจากตัวแยกประเภทไบนารีสามตัวเปรียบเทียบกับตัวแยกประเภทไบนารีสองตัวในวิธี "หนึ่งเทียบกับทั้งหมด"


1

ฉันต้องบอกว่าฉันไม่พบวรรณกรรมใด ๆ เกี่ยวกับหัวข้อนี้ เท่าที่ฉันรู้สิ่งที่คุณถามเป็นไปไม่ได้ คุณควรตระหนักถึงสิ่งนี้และเจ้าของผลิตภัณฑ์ควรเป็นเช่นกัน เหตุผลก็คือฟังก์ชั่นการสูญเสียใด ๆ ขึ้นอยู่กับฉลากที่รู้จักดังนั้นจึงไม่มีวิธีที่คุณสามารถทำนายฉลากที่ไม่ได้อยู่ในข้อมูลการฝึกอบรม นอกจากนี้ยังเป็นนิยายวิทยาศาสตร์ที่อัลกอริทึมการเรียนรู้ด้วยเครื่องจักรสามารถทำนายบางสิ่งที่ไม่ได้รับการฝึกฝน

ต้องบอกว่าฉันคิดว่าอาจมีวิธีแก้ปัญหา (ให้ฉันชี้ให้เห็นว่านี่เป็นความเห็นที่ไม่ได้ขึ้นอยู่กับวรรณกรรมอย่างเป็นทางการ) ถ้าลักษณนามเป็นความน่าจะเป็นผลลัพธ์คือความน่าจะเป็นสำหรับแต่ละคลาสที่เป็นจริงและการถอดรหัสจะเป็นโพรบที่สูงกว่า บางทีคุณสามารถตั้งค่าขีด จำกัด สำหรับความน่าจะเป็นเช่นนั้นแบบจำลองคาดการณ์ "ไม่ทราบ" หากความน่าจะเป็นทั้งหมดต่ำกว่าขีด จำกัด นั้น ผมขอยกตัวอย่างให้คุณ

ปล่อย M(x) เป็นต้นแบบ 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ที่ปรากฏในสตรีมควรได้รับการคาดการณ์เป็น นี่เป็นเรื่องปกติในการประมวลผลภาษาธรรมชาติ (NLP) เนื่องจากมีโทเค็นคำใหม่ปรากฏอยู่เสมอในสตรีมคำ

  2. ฝึกฝนรูปแบบใหม่ทุกครั้งที่มีหมวดหมู่ใหม่ปรากฏขึ้น

เมื่อคุณพูดถึงSGDClassifierฉันถือว่าคุณใช้ scikit เรียนรู้ Scikit-learning ไม่สนับสนุนการเรียนรู้ออนไลน์เป็นอย่างดี มันจะดีกว่าที่จะเปลี่ยนกรอบที่ให้การสนับสนุนที่ดีกว่าสตรีมมิ่งและการเรียนรู้ออนไลน์เช่นSpark

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.