Diese Lösung wurde für den Wettbewerb „LMSYS – Chatbot Arena Human Preference Predictions“ auf Kaggle entwickelt, bei dem die Teilnehmer aufgefordert wurden, Benutzerpräferenzen in direkten Gesprächen zwischen Chatbots vorherzusagen, die auf großen Sprachmodellen (LLMs) basieren. Die Aufgabe bestand darin, einen Datensatz von Chatbot Arena zu nutzen, bei dem Benutzer mit zwei anonymen LLMs interagieren und ihre bevorzugte Antwort auswählen. Durch die Entwicklung eines maschinellen Lernmodells, das diese Präferenzen genau vorhersagt, wollten wir dazu beitragen, die Ausrichtung von Chatbot-Antworten auf menschliche Präferenzen zu verbessern.
Unser Team belegte erfolgreich den 4. Platz von 1849 Teams und erhielt für unsere Lösung eine Goldmedaille und einen Preis von 20.000 US-Dollar! ?
Zuerst verwendeten wir den offiziellen Datensatz (55.000) zusammen mit 33.000 deduplizierten Daten, wobei wir eine 20-fache Kreuzvalidierung (n_splits=20) verwendeten, aber nur auf einer Falte trainierten, um die Menge an Trainingsdaten zu maximieren. Zusätzlich haben wir Pseudo-Labels für 30.000 Einträge aus dem Ultrafeedback-Datensatz erstellt, um den Datensatz weiter zu ergänzen.
Wir haben eine einzigartige Eingabeaufforderung entwickelt, die von Vorteil ist, denn wenn die Dialoglänge die maximale Tokenlänge ( max_length
) überschreitet, ermöglicht sie eine angemessene Verkürzung der letzten Gesprächsrunde. Dadurch wird sichergestellt, dass die Eingabeaufforderung, die Antwort A und die Antwort B alle angemessen angezeigt werden können, wodurch Situationen vermieden werden, in denen nur die Eingabeaufforderung oder Antwort A abgeschnitten wird. Wenn die verbleibende Tokenanzahl in der letzten Runde weniger als 80 beträgt, wird die gesamte Konversationsrunde (und die folgenden) verworfen. Diese Schwellenwerte und Anteile wurden durch Beobachtung des Trainingssatzes bestimmt.
def tokenize_cls_p3 ( example , tokenizer , max_length , is_train ):
input_ids = []
attention_mask = []
dot_tokens = tokenizer ( "......" , add_special_tokens = False )[ "input_ids" ]
final_p_tokens = tokenizer ( " n n --- n Which response is better? [A or B or tie] n Answer: " , add_special_tokens = False )[ "input_ids" ]
for ps , ras , rbs in zip ( example [ 'prompt' ], example [ 'response_a' ], example [ 'response_b' ]):
one_input_ids = [ tokenizer . bos_token_id ]
prev_tokens_num = 2 + len ( final_p_tokens ) # 2 for bos_token and eos_token
for idx , ( p , ra , rb ) in enumerate ( zip ( ps , ras , rbs )):
r_tokens = tokenizer ( f' n n ## Round { idx + 1 } :' if idx else f'## Round { idx + 1 } :' , add_special_tokens = False )[ "input_ids" ]
p_tokens = tokenizer ( f' n ### Prompt: n { p } ' , add_special_tokens = False )[ "input_ids" ]
ra_tokens = tokenizer ( f' n n ### Response A: n { ra } ' , add_special_tokens = False )[ "input_ids" ]
rb_tokens = tokenizer ( f' n n ### Response B: n { rb } ' , add_special_tokens = False )[ "input_ids" ]
all_tokens_num = prev_tokens_num + len ( r_tokens ) + len ( p_tokens ) + len ( ra_tokens ) + len ( rb_tokens
if all_tokens_num > max_length :
remain_tokens_num = max_length - prev_tokens_num - len ( r_tokens ) - 3 * len ( dot_tokens )
if remain_tokens_num >= 80 :
p_tokens = p_tokens [: int ( remain_tokens_num * 0.2 )] + dot_tokens if len ( p_tokens ) > int ( remain_tokens_num * 0.2 ) else p_tokens
ra_tokens = ra_tokens [: int ( remain_tokens_num * 0.4 )] + dot_tokens if len ( ra_tokens ) > int ( remain_tokens_num * 0.4 ) else ra_tokens
rb_tokens = rb_tokens [: int ( remain_tokens_num * 0.4 )] + dot_tokens if len ( rb_tokens ) > int ( remain_tokens_num * 0.4 ) else rb_tokens
one_input_ids += r_tokens + p_tokens + ra_tokens + rb_tokens
break
else :
prev_tokens_num = all_tokens_num
one_input_ids += r_tokens + p_tokens + ra_tokens + rb_tokens
one_input_ids += final_p_tokens + [ tokenizer . eos_token_id ]
one_attention_mask = [ 1 ] * len ( one_input_ids )
input_ids . append ( one_input_ids )
attention_mask . append ( one_attention_mask )
if is_train :
labels = [ 0 if a_win else 1 if b_win else 2 for a_win , b_win , tie in zip ( example [ 'winner_model_a' ], example [ 'winner_model_b' ], example [ 'winner_tie' ])]
return {
"input_ids" : input_ids ,
"attention_mask" : attention_mask ,
"labels" : labels ,
}
else :
return {
"input_ids" : input_ids ,
"attention_mask" : attention_mask ,
}
Wir haben gemma-2-9b-it als Startmodell ausgewählt, das andere Modelle wie Llama3 8b und Llama3.1 8b deutlich übertrifft. Wir haben Gemma2ForSequenceClassification für eine Klassifizierungsaufgabe mit drei Klassen verwendet und das Modell mithilfe von Lora mit bf16- Präzision verfeinert. Die besten experimentellen Ergebnisse wurden auf vier A100-GPUs erzielt.
Jedes Experiment dauerte ungefähr 10 Stunden für die erste Phase und 15 Stunden für die zweite Phase auf einem System mit 4 A100-GPUs (40G).
Die Inferenzphase verwendet eine ähnliche Codestruktur wie die Trainingsphase, mit einigen wesentlichen Unterschieden: Die max_length
wird auf 3072 erhöht, und Antwort_a und Antwort_b werden im Rahmen einer TTA-Strategie (Test-Time Augmentation) vertauscht. Das Endergebnis ist die durchschnittliche Leistung beider.
Die Nachbearbeitung wurde für zwei spezifische Szenarien angewendet (die sich überschneiden können):
df2 = pd . read_csv ( '/kaggle/input/lmsys-chatbot-arena/test.csv' )
df2 [ 'id' ] = df2 [ 'id' ]. astype ( str )
a_null_df = df2 [( df2 [ "response_a" ] == '[null]' ) | ( df2 [ "response_a" ] == '[]' ) | ( df2 [ "response_a" ] == '[ ]' ) | ( df2 [ "response_a" ] == '[ ]' ) | ( df2 [ "response_a" ] == '[""]' ) | ( df2 [ "response_a" ] == '["",""]' )]
a_null_id_list = a_null_df [ "id" ]. tolist ()
submission_df . loc [ submission_df [ 'id' ]. isin ( a_null_id_list ), [ 'winner_model_a' , 'winner_model_b' , 'winner_tie' ]] = [ 0.04 , 0.88 , 0.08 ]
b_null_df = df2 [( df2 [ "response_b" ] == '[null]' ) | ( df2 [ "response_b" ] == '[]' ) | ( df2 [ "response_b" ] == '[ ]' ) | ( df2 [ "response_b" ] == '[ ]' ) | ( df2 [ "response_b" ] == '[""]' ) | ( df2 [ "response_b" ] == '["",""]' )]
b_null_id_list = b_null_df [ "id" ]. tolist ()
submission_df . loc [ submission_df [ 'id' ]. isin ( b_null_id_list ), [ 'winner_model_a' , 'winner_model_b' , 'winner_tie' ]] = [ 0.88 , 0.04 , 0.08 ]
same_a_b_df2 = df2 [( df2 [ "response_a" ] == df2 [ "response_b" ])]
same_a_b_id_list = same_a_b_df2 [ "id" ]. tolist ()
submission_df . loc [ submission_df [ 'id' ]. isin ( same_a_b_id_list ), [ 'winner_model_a' , 'winner_model_b' , 'winner_tie' ]] = [ 0.06 , 0.06 , 0.88 ]
Überblick : Entwicklung und Optimierung eines menschlichen Präferenzvorhersagemodells für Dialogsysteme basierend auf dem Gemma-2-9b-it-Modell, wodurch die Genauigkeit der Vorhersage von Benutzerpräferenzreaktionen im Dialogsystem verbessert wird.
Schlüsseltechniken :
Daoyuan Li – Kaggle-Profil
Bei Fragen wenden Sie sich bitte an Daoyuan Li unter [email protected].