top of page

Rumo a modelos que podem descrever o código

Construímos um modelo de linguagem chamado Gemini que pode ler código e descrever sua funcionalidade em linguagem natural.  Nosso modelo é compatível com uma variedade de linguagens de programação e é multilíngue. Neste blog, detalhamos suas capacidades, bem como os possíveis impactos.

2 de janeiro de 2022

Project Gemini (17).png

Construímos um modelo de linguagem chamado Gemini que foi treinado em aproximadamente 1 milhão de pares de descrição de código. O objetivo deste projeto era simples: podemos habilitar as máquinas com a capacidade de descrever o código de maneira eficaz da mesma forma que um ser humano poderia? Embora seja uma tarefa difícil, trabalhos recentes em modelos de linguagem fizeram avanços rápidos, tornando tarefas anteriormente difíceis muito mais plausíveis.

No entanto, a construção de modelos de linguagem para código de computador apresenta mais desafios do que os modelos de linguagem tradicionais. Primeiro, a confiabilidade dos dados é bastante variável e não tão abundante. Normalmente, os conjuntos de dados disponíveis publicamente contêm amostras de código de qualidade média, mas ficam aquém quando contêm descrições de qualidade. Isso pode distorcer negativamente o desempenho de um modelo no início, o que é difícil de reparar mais adiante no ciclo de desenvolvimento. Em segundo lugar, alcançar modelos de linguagem de alto desempenho em qualquer tarefa tornou-se cada vez mais caro. Está bem documentado que quanto maior for um modelo de linguagem em parâmetros treináveis, melhor será seu desempenho; No entanto, essa estratégia vem com custos monetários e de computação mais pesados.

Outro grande desafio ao construir um modelo de linguagem é a confiabilidade do sistema como um todo. Trabalhos recentes de empresas como OpenAI, Google e Facebook mostraram que os modelos de linguagem, particularmente no domínio da geração de linguagem, podem produzir resultados inesperados. O controle das saídas geradas ainda é um problema de pesquisa em aberto que ainda precisa ser resolvido. 

Com todos os desafios mencionados, as questões ficaram mais claras para construir um sistema de sucesso para descrever o código:

1.Podemos selecionar amostras de dados suficientes que contenham descrições de código de qualidade com o código original?
2. Como podemos construir um modelo de linguagem menor que maximize consistência e fidelidade ao mesmo tempo?

Construindo um modelo para descrever o código


Modelo
Para resolver as questões mencionadas acima, o Gemini usa a popular arquitetura de transformadores. Descobrimos que o treinamento com qualquer outro tipo de arquitetura (ou seja: RNN ou GRU's) não rendeu a mesma qualidade que um transformador renderia. 

Primeiro, um dos principais focos da construção de um sistema para descrever o código era adicionar consistência às saídas geradas. O Gemini usa uma estrutura de codificador-decodificador, em oposição a uma estrutura somente decodificadora como GPT-3. Descobrimos que o uso de uma estrutura de codificador-decodificador melhorou drasticamente a consistência das descrições geradas em comparação com o uso apenas de decodificadores. 

Gemini consiste em 3 tamanhos de modelos diferentes. O principal consiste em 600 milhões de parâmetros, uma versão menor "Lambda" com 230 milhões de parâmetros e "Plato" em 64 milhões de parâmetros. Isso pode ser uma surpresa para alguns leitores, dada a escala recente de projetos como GPT-3, que usam até 175 bilhões de parâmetros; No entanto, descobrimos que mais parâmetros só produziam melhor desempenho quando combinados com o ajuste fino. 


conjunto de dados
Em segundo lugar, encontrar dados de qualidade foi um grande desafio. Nosso objetivo com o Gemini é fornecer resumos naturais de código; Como não há conjuntos de dados publicamente disponíveis de amostras de código com uma descrição correspondente natural, tivemos que gerar amostras sinteticamente. Começamos com o primeiro pré-treinamento do Gemini em 885 mil amostras publicamente disponíveis de pares code-docstring por 7 épocas. Isso deu ao Gemini uma boa base para entender a semântica do código ao traduzir para o inglês.

Para obter naturalidade, preparamos manualmente 250 amostras manuscritas com seu snippet de código correspondente. Esses novos pontos de dados foram usados para ajustar uma nova versão do Gemini por 2 épocas, o que lhe deu a capacidade de escrever naturalmente. Nossa intenção neste ponto era permitir que o Gemini anotasse novamente nosso conjunto de dados original, no entanto, ele só poderia escrever cerca de 3 em 10 descrições em um nível satisfatório. 

Com ajuda humana, o Gemini conseguiu gerar sinteticamente 700 descrições, o que nos permitiu ajustar o modelo novamente. Desta vez, descobrimos que o Gemini poderia descrever o código com muito mais clareza. Repetimos esse ciclo até que a Gemini produzisse 10.000 amostras para Python, Javascript, Go, PHP, Java e Ruby. 

Screenshot 2021-12-30 5.12.02 PM.png

A figura à esquerda/acima é um exemplo visual da nossa estratégia de geração de dados sintéticos, essencial para atingir o desempenho do Gemini. A geração de dados sintéticos de qualidade também é importante para manter a segurança do código do usuário. Como temos um pipeline robusto de geração de dados, não é necessário usar o código do usuário em nossos modelos. 

Descobrimos que os modelos só precisavam ser ajustados por no máximo 3 épocas, independentemente do tamanho do modelo. No futuro, esperamos exigir apenas pré-treinamento ou ajuste fino para novas tarefas.  

Amostras

As amostras exibidas à direita/abaixo foram retiradas de nossos testes internos iniciais de todos os três modelos. Embora essas amostras sejam escolhidas a dedo por nós, acreditamos que elas representam com precisão as habilidades de nosso modelo. Para melhorar o desempenho, utilizamos um método de pesquisa "best of" ao gerar saídas. Descobrimos que todos os modelos têm melhor desempenho ao escolher entre os 3 ou 4 melhores resultados. Além disso, descobrimos que definir uma penalidade de repetição de 2,5 a 3 produziu os melhores resultados ao tentar maximizar a coerência. 

Nota: n denota o número "melhor de" (por exemplo, pegue a melhor solução de 3 saídas geradas) ep denota penalidade de repetição.

Gêmeos (602 milhões de parâmetros)
n = 3  p = 3,0

Código de entrada

pedidos de importação
importar json

def post_url(endpoint, json_data):
    r = requests.post(endpoint, data=json.dumps(json_data))
    return r.text

Descrição gerada:

Este programa envia um POST para um determinado endpoint e retorna a resposta do servidor JSON.

experimentos

Ao construir o Gemini, tivemos muitas perguntas sobre o que afetaria mais o desempenho. Consideramos um modelo de alto desempenho como aquele que pode escrever saídas coerentes e sensatas que cobrem o escopo do código. Trabalhos anteriores neste domínio mostraram que modelos de linguagem maiores (em termos de parâmetros) melhoram significativamente o desempenho, então treinamos uma variedade de tamanhos de modelo.

Nossas maiores dúvidas eram as seguintes:

 

  • Como o tamanho do modelo afetaria as saídas geradas?

  • O pré-treinamento + ajuste fino superam o pré-treinamento estrito?

  • Quão bem cada modelo generaliza se apenas pré-treinado e ajustado em uma linguagem de programação? (ou seja, um modelo treinado apenas em Python pode generalizar para Javascript, Java etc.)

  • Podemos reduzir o número de épocas de treinamento necessárias?​

Descobertas

Em nossos experimentos, descobrimos que simplesmente aumentar o número de parâmetros treináveis não se correlaciona com um aumento significativo no desempenho. Em vez disso, descobrimos que o pré-treinamento e o ajuste fino geram o melhor desempenho, o que foi bem documentado em trabalhos anteriores.

Para nossa surpresa, descobrimos que apenas o ajuste fino em uma linguagem de programação é competitivo com o ajuste fino em várias linguagens de programação. Nossa teoria é que o Gemini presta atenção à semântica do código (ou seja, nomes de variáveis, nomes de funções, nomes de bibliotecas, etc.) em vez de pura memorização. Isso nos leva a acreditar que um modelo maior que é pré-treinado e ajustado em um amplo escopo de uma linguagem de programação pode superar substancialmente a versão atual do Gemini. 

Modelos maiores tiveram uma vantagem no número de épocas de treinamento necessárias. De um modo geral, Gêmeos precisou apenas de cerca de 5 épocas de pré-treinamento e 1 a 2 épocas de ajuste fino para obter resultados de qualidade. 

Código de entrada

importar * como tf de '@tensorflow/tfjs';

 

exportar função assíncrona loadModel() {

   const model = await tf.loadModel('/model/model.json');

   return modelo;

}

 

exportar função assíncrona startServer() {

    const model = await loadModel();

    servidor const = http.createServer(async (req, res) => {

 

    if (req.method === 'POST') {

      const body = await req.json();

      const input = tf.tensor2d(body.input, [1, body.input.length]);

      saída const = model.predict(input);

      res.json(output.dataSync());

    }

  });

  server.listen(8080);

}

Gêmeos (602 milhões de parâmetros, pré-treinados + ajustados)

Este programa é usado para criar um servidor para o modelo. Ele primeiro carrega o modelo e, em seguida, inicia um ouvinte no servidor.

Gemini (602 milhões de parâmetros, pré-treinamento sem ajuste fino)

Carrega um modelo do TensorFlow e inicia o servidor para solicitações.

Gemini (1,4 bilhão de parâmetros, pré-treinamento sem ajuste fino)

Este programa faz o seguinte: primeiro carrega o modelo. Em seguida, ele inicia um ouvinte do servidor. Por último, ele inicia um servidor para solicitações.

Gemini (662 milhões de parâmetros, pré-treinamento em vários idiomas, ajuste fino apenas em Python)

Esta função é usada para iniciar um servidor para o modelo. Ele primeiro carrega o modelo e depois inicia o servidor.

Áreas para melhoria

Durante nossa experimentação, encontramos algumas deficiências comuns do Gemini e das versões menores. A maioria das deficiências tende a seguir o trabalho anterior no espaço de modelagem de linguagem, o que era esperado em alguma capacidade. Algumas das deficiências são as seguintes:
 

As saídas são ocasionalmentetambém conciso
Quando colocado com entradas mais longas, Gêmeos resumirá a descrição em 7 palavras ou menos. Geralmente, isso não seria um problema, mas achamos que essas descrições não são claras ou não cobrem todo o escopo da entrada. 

As descrições terão alta repetibilidade em determinados cenários 

Um problema comum na modelagem de linguagem são palavras ou sentenças repetidas. Descobrimos que o Gemini se envolverá nesse comportamento quando colocado em um contexto misto (ou seja: entradas que possuem diferentes tipos de código misturados).

Gemini é computacionalmente caro para treinar

O pré-treinamento Gemini por 5 a 7 épocas requer um dia inteiro usando uma GPU Nvidia A100. Isso pode ser muito demorado e caro para uma startup fazer de forma consistente.

Lixo dentro ainda é igual a lixo fora

Embora esse não seja um comportamento surpreendente, o Gemini não consegue descrever bem o código se ele estiver mal escrito. Esperamos poder preencher essa lacuna com novos métodos ou mais dados no futuro.

A janela de contexto do Gemini é uma limitação

Em sua versão atual, o Gemini pode aceitar apenas 512 tokens como entrada. Isso pode ser limitante em certos cenários onde o resumo de programas completos.

O que vem a seguir para Gêmeos?

Acreditamos que o Gemini pode ser o início de uma tecnologia altamente impactante, a ponto de uma mudança potencialmente importante na forma como o software é desenvolvido. Os desenvolvedores de software geralmente gostam de escrever código e construir novas tecnologias; Muitas partes do desenvolvimento de software prejudicam a escrita do código, como documentação, revisão e correção de bugs. Um sistema como o Gemini pode ser usado de várias maneiras para automatizar as partes não agradáveis de escrever software e permitir que mais tempo seja focado em maneiras melhores.

Estamos incrivelmente empolgados para ver onde o Gemini pode ser usado. Em um futuro próximo, prevemos que Gêmeos será usado das seguintes maneiras:

 

  • Integração mais rápida para equipes de software de qualquer tamanho(ou seja, aumentando a velocidade de compreensão da base de código)

  • Automação de práticas internas de software(ex. revisão de código, correção de bugs, documentação, etc.)

  • Quebrando as barreiras linguísticas para equipes multilíngues/remotas

  • Instituições educacionais(Aumentando a velocidade que os alunos podem entender o código)

  • Projetos de código aberto(removendo a ambigüidade da funcionalidade)

 

No entanto, nossa visão para o Gemini é muito maior do que uma simples explicação de código. No futuro, queremos expandir os recursos do Gemini para:

 

  • Escreva documentos completos, como README ou relatórios sobre a funcionalidade do código

  • Pergunta respondendo sobre código

  • Criando diferentes níveis de explicação(ex. uma descrição de código para um desenvolvedor experiente pode ser diferente de um para um estudante do ensino médio)​​

  • Escrever exemplos de como o código pode ser usado na prática(ou seja: gerando exemplos de chamada de função)

A partir desses exemplos, pode-se começar a imaginar o amplo escopo de um código geral em texto como Gemini.

Filosofia de lançamento

Em última análise, nosso objetivo é distribuir o poder de fortes sistemas de aprendizado de máquina para o maior número possível de indivíduos, grupos e organizações. Como sistemas como o Gemini são caros para desenvolver, estamos lançando-o como um produto pago. Permitiremos que qualquer pessoa acesse o Gemini (e outras versões) por meio de:

  • Um produto totalmente gerenciado e sem código

  • API do desenvolvedor

  • Licenciamento do produto

  • Parcerias


Tudo isso será usado para financiar a próxima geração de nossos motores. Nos próximos anos, planejamos disponibilizar várias versões do Gemini por meio de código aberto, pois acreditamos fortemente em permitir que qualquer pessoa use nossa tecnologia. No entanto, os cronogramas não são garantidos. 

bottom of page