Esta solução foi desenvolvida para a competição LMSYS - Chatbot Arena Human Preference Predictions no Kaggle, onde os participantes foram desafiados a prever as preferências do usuário em conversas diretas entre chatbots alimentados por grandes modelos de linguagem (LLMs). A tarefa envolveu a utilização de um conjunto de dados do Chatbot Arena , no qual os usuários interagem com dois LLMs anônimos e escolhem sua resposta preferida. Ao criar um modelo de aprendizado de máquina que prevê com precisão essas preferências, pretendemos contribuir para melhorar o alinhamento das respostas do chatbot com as preferências humanas.
Nossa equipe ficou em 4º lugar entre 1.849 equipes , ganhando uma medalha de ouro pela nossa solução e um prêmio de US$ 20.000! ?
Primeiro, utilizamos o conjunto de dados oficial (55k) junto com 33k de dados desduplicados, empregando uma validação cruzada de 20 vezes (n_splits=20), mas treinamos apenas uma vez para maximizar a quantidade de dados de treinamento. Além disso, criamos pseudo-rótulos para 30.000 entradas do conjunto de dados de ultrafeedback para complementar ainda mais o conjunto de dados.
Projetamos um prompt exclusivo, o que é benéfico porque quando a duração do diálogo excede o comprimento máximo do token ( max_length
), permite um truncamento razoável da rodada final da conversa. Isso garante que o prompt, a resposta A e a resposta B possam ser exibidos adequadamente, evitando situações em que apenas o prompt ou a resposta A sejam truncados. Se a contagem de tokens restantes na rodada final for inferior a 80, toda a rodada de conversação (e as subsequentes) será descartada. Esses limiares e proporções foram determinados através da observação do conjunto de treinamento.
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 ,
}
Selecionamos gemma-2-9b-it como modelo inicial, que supera significativamente outros modelos, como Llama3 8b e Llama3.1 8b . Usamos Gemma2ForSequenceClassification para uma tarefa de classificação de três classes e ajustamos o modelo usando lora com precisão bf16 . Os melhores resultados experimentais foram alcançados em quatro GPUs A100.
Cada experimento durou aproximadamente 10 horas para a primeira fase e 15 horas para a segunda fase em um sistema com 4 GPUs A100 (40G).
A fase de inferência usa uma estrutura de código semelhante à fase de treinamento, com algumas diferenças importantes: max_length
é aumentado para 3072 e response_a e response_b são trocados como parte de uma estratégia de aumento de tempo de teste (TTA). O resultado final é a produção média de ambos.
O pós-processamento foi aplicado para dois cenários específicos (que podem se sobrepor):
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 ]
Visão geral : Desenvolvi e otimizei um modelo de previsão de preferência humana para sistemas de diálogo baseado no modelo gemma-2-9b-it, melhorando a precisão da previsão das respostas de preferência do usuário no sistema de diálogo.
Técnicas principais :
Daoyuan Li - Perfil Kaggle
Para qualquer dúvida, entre em contato com Daoyuan Li em [email protected].