paint-brush
Software para Exploração: Alcançando Equilíbrio na Descobertapor@sinavski
331 leituras
331 leituras

Software para Exploração: Alcançando Equilíbrio na Descoberta

por Oleg SInavski10m2024/03/21
Read on Terminal Reader

Muito longo; Para ler

Uma postagem defendendo: - evitar demasiadas técnicas de produção durante a pesquisa. Produção e pesquisa têm objetivos diferentes - não há problema em assumir "dívidas tecnológicas" em pesquisa, já que a maior parte do código vai morrer. Por exemplo, você não deve se esforçar para reutilizar código - mas como pesquisador você ainda deve investir em exploração rápida, ramificação rápida e código simples e limpo
featured image - Software para Exploração: Alcançando Equilíbrio na Descoberta
Oleg SInavski HackerNoon profile picture

Trabalhei com pesquisa toda a minha vida, então conheço um estereótipo de que pesquisadores escrevem códigos feios (por exemplo, veja aqui , aqui ou aqui ). Mas pensei: podemos consertar, certo? Então, várias vezes tentei projetar boas estruturas de pesquisa. Tentei trazer interfaces e criar boas abstrações usando livros de engenharia de software e blogs que gostava de ler.


Mas repetidamente todos esses esforços foram em vão. A maioria dos softwares de pesquisa em que trabalhei nunca foi para produção (embora alguns tenham entrado). Teria sido ótimo para minha saúde mental se alguém me contasse uma verdade simples: morrer código de pesquisa é na verdade o que deveria acontecer . Os pesquisadores não deveriam gastar muito tempo projetando-o em primeiro lugar.


Engenheiros de software profissionais sempre desprezam os pesquisadores que não usam as melhores práticas de software. Existem vários posts tentando elevar o nível do código de pesquisa (por exemplo, este excelente post e um manual de código de pesquisa ). Mas este post vai ao contrário: discute como não exagerar nas melhores práticas de software e, em vez disso, investir apenas na exploração rápida. É direcionado para empresas voltadas para pesquisa onde seu objetivo é testar muitas ideias rapidamente.

1. Assuma algumas dívidas tecnológicas estratégicas

Um projeto de pesquisa bem-sucedido em uma empresa tem duas fases: exploração e aproveitamento. Na “exploração”, você deseja experimentar tantas soluções diversas quanto possível. Durante a “exploração” você tem que fortalecer a melhor solução e transformá-la em um produto útil.

Durante a exploração, muitos projetos morrem. Você deve construir uma solução robusta somente durante a exploração.

As práticas ideais de software são bastante diferentes entre os dois. É por isso que as empresas costumam ter divisões separadas de pesquisa e de produtos. Todos os livros que você normalmente lê sobre design de software são principalmente sobre a segunda fase de “exploração”. Nesta fase, você está construindo as bases para um produto escalonável. É aqui que entram todos os padrões de design: APIs legais, registro, tratamento de erros e assim por diante.


Mas na primeira fase de “exploração” você não está construindo fundações que durarão para sempre. Na verdade, se a maioria dos seus esforços sobreviver, então você (por definição) não explorou o suficiente.


Muitas práticas neste post são exemplos do que normalmente se tornaria “dívida tecnológica”. É o que você ganha ao não escrever código limpo, reutilizável e bem abstraído. A dívida é sempre ruim? Preferimos nunca obter um empréstimo ou hipoteca, mas pedir dinheiro emprestado costuma ser uma boa estratégia na vida. Não há problema em se endividar para agir rapidamente e lucrar mais tarde.

Não há problema em contrair dívidas de software em pesquisa – você não precisa pagar tudo, apenas a minoria nos caminhos de pesquisa bem-sucedidos.

Da mesma forma, ao não assumir dívidas técnicas, você pode atrasar sua pesquisa. A boa notícia é que na maioria das vezes você não precisa pagar. A maior parte do seu código de pesquisa provavelmente morrerá de qualquer maneira. Portanto, em média, você não sofrerá com toda a dívida tecnológica que contraiu.

O caso contra a reutilização de código

Muitas arquiteturas de software e técnicas de refatoração são orientadas especificamente para melhorar a reutilização de código. Existem desvantagens genéricas na reutilização de código. Mas na produção eles são superados pelos benefícios bem conhecidos (por exemplo, veja este post típico ). Em projetos de pesquisa, a maior parte do código está destinada a cair no esquecimento. Esforçar-se pela reutilização de código pode, na verdade, atrasá-lo.


Limitar a reutilização de código é o tipo de dívida técnica que pode ser assumida em pesquisa. Há vários padrões de reutilização de código que quero discutir: adição de uma dependência desnecessária, cópia de código, manutenção de muitos códigos de pesquisa compartilhados, investimentos prematuros em design.

Pense duas vezes antes de importar algo novo

Se você conhece uma biblioteca com versões bem mantidas que irá acelerar você - vá em frente! Mas antes de assumir uma nova dependência, tente avaliar se vale a pena. Cada adicional o aproxima do inferno da dependência. Isso faz com que você invista tempo aprendendo e solucionando problemas. Veja mais armadilhas das dependências neste post conciso .


Provavelmente não há problema em depender de algo se:

  • você já usou, não há muito o que aprender, tem uma comunidade grande, bons documentos e testes
  • é versionado, fácil de instalar
  • e, finalmente, não há como implementá-lo sozinho.

Mas tenha cuidado com uma dependência se:

  • você não consegue descobrir como usá-lo rapidamente, é muito novo (ou muito antigo) ou ninguém parece saber sobre ele; não há documentos ou testes

  • é do seu monorepo e está sendo constantemente alterado por outras equipes

  • ele traz muitas outras dependências e ferramentas; ou é apenas difícil de instalar

  • e, finalmente, você sente que (ou algum LLM) pode escrever esse código em algumas horas.


Em vez de uma dependência explícita, você pode seguir um belo provérbio Go: “ um pouco de cópia é melhor do que um pouco de dependência ”, que é o nosso próximo tópico.

Copypaste oferece liberdade de experimentação

Copiar é rápido e às vezes é a melhor ferramenta durante a pesquisa.

Alguns dizem que “ copiar e colar deveria ser ilegal ”. Mas, para minha surpresa, acabei argumentando a favor disso com bastante frequência. Copypaste pode ser a escolha ideal durante a fase de exploração.


Se você depende de uma função muito usada de outra parte da base de código, pode esquecer de alterá-la facilmente. É provável que você quebre algo para alguém e precise gastar um tempo precioso em revisões e correções de código. Mas se você copiar e colar o código necessário em sua pasta, poderá fazer o que quiser com ele. Isto é importante em projetos de pesquisa onde a experimentação é uma norma e não uma exceção. Especialmente se você não tiver certeza se as mudanças serão úteis para todos.


Acho que as bases de código de aprendizado profundo são mais adequadas para copiar e colar. Normalmente, a quantidade de código necessária para descrever um modelo e seu treinamento não é tão grande. Mas, ao mesmo tempo, pode ser muito matizado e difícil de generalizar. Scripts de treinamento compartilháveis tendem a crescer para um tamanho incontrolável: por exemplo, Hugging Face transformers Trainer tem +4k linhas. Curiosamente, os transformadores optaram por copiar e colar no nível do modelo. Confira a postagem deles com o raciocínio por trás da política de "modelo de arquivo único". Veja mais recursos sobre a beleza de copiar e colar no final.


Uma alternativa ao copypaste é permanecer em uma filial. Mas sinto que isso traz muita sobrecarga no trabalho em equipe. Além disso, encontrei vários outros posts sobre a beleza do copypaste - veja mais posts na conclusão.

Manter o código de pesquisa compartilhado é difícil

A manutenção de código compartilhado muito utilizado requer muito trabalho. Dê uma olhada no número torch.nn.Module de linhas de arquivo plotadas em relação à versão Pytorch . Você pode ver que mesmo as equipes de pesquisa mais avançadas lutam para manter a complexidade sob controle.

Comprimento do arquivo torch.nn.Module dependendo da versão do PyTorch. Não está ficando mais simples.

Não subestime o tempo e os recursos necessários para manter um grande código de pesquisa compartilhado. Quanto mais uma biblioteca de pesquisa é usada, mais complicada ela se torna. Isso acontece mais rápido do que em uma biblioteca típica porque cada direção de pesquisa tem um caso de uso ligeiramente diferente. Estabeleça regras muito rígidas sobre o que poderia ser contribuído de volta. Caso contrário, o código compartilhado se tornará frágil e repleto de opções, otimizações com erros e casos extremos. Como a maior parte do código de pesquisa desaparece, toda essa complexidade extra nunca mais será usada. Eliminar parte do seu código compartilhado liberará algum tempo para fazer pesquisas reais.

Projete para exploração, não para reutilização de código

É verdade que você não deseja preparar muito seu código para o futuro, mesmo em produção. Tente implementar a solução mais simples possível que atenda aos requisitos. Mas no código de produção sempre há aspectos de manutenção a serem considerados. Por exemplo, tratamento de erros, velocidade, registro e modularização é o que normalmente você precisa pensar.


No código de pesquisa, nada disso importa. Você só quer provar rapidamente que uma ideia é boa ou ruim da maneira mais rápida possível e seguir em frente. Portanto, a simplicidade suja que se consegue sem quaisquer módulos ou APIs está totalmente ok!


Não perca tempo valioso com investimentos prematuros em software, como:

  • criar interfaces de componentes muito cedo no projeto. Você gastará muito tempo se adaptando às restrições artificiais criadas por você mesmo
  • otimizando a infraestrutura de treinamento de aprendizagem profunda antes de se comprometer com a solução de aprendizagem profunda
  • usando sistemas de configuração/fábricas/serialização de produção ou classes base. Muitas vezes você não precisa da funcionalidade deles durante a prototipagem
  • sistemas excessivamente rígidos de linting e verificação de tipo. Não há razão para desacelerar o código de pesquisa descartável em rápida mudança.

2. Invista na exploração rápida

O objetivo de um projeto de pesquisa é encontrar uma nova solução. Ninguém sabe (por definição) como é. É semelhante a um processo de otimização em um cenário de pesquisa complicado e com informações limitadas. Para encontrar um bom mínimo, você precisa tentar vários caminhos, reconhecer caminhos bons e ruins e não ficar preso em mínimos locais. Para fazer tudo isso rapidamente, às vezes você precisa fazer investimentos em software em vez de contrair dívidas tecnológicas.

Acelere caminhos comuns

Invista na aceleração de partes comuns de seus projetos de pesquisa.

Existem vários caminhos de pesquisa diferentes que você deseja tentar. Existe um design, uma biblioteca ou uma otimização que economizaria tempo na maioria dos caminhos? Você deve ter cuidado para não projetar demais nada, porque nem sempre conhece todas as ideias que está prestes a experimentar. Isso é muito personalizado para cada projeto, mas aqui estão alguns exemplos:


  • se você treina redes profundas, invista em infraestrutura de treinamento. Descubra hiperparâmetros que permitem convergir durante o treinamento de forma rápida e confiável
  • se cada experimento exigir que você use um modelo diferente, descubra como você pode trocá-los rapidamente (por exemplo, usando um sistema de fábrica simples ou apenas copiar e colar)
  • se cada experimento tiver muitos parâmetros e for difícil de gerenciar, invista em uma boa biblioteca de configuração.

Ramifique rapidamente

Invista na velocidade de iniciar novos caminhos de pesquisa. Você precisa de muitas direções diversas para encontrar a solução.

Os pesquisadores devem ser capazes de iniciar novas ideias diversas rapidamente. Parece fácil no início do projeto. Mas depois torna-se gradualmente cada vez mais difícil à medida que as pessoas se enraízam nos seus caminhos de investigação favoritos. Para resolver isso, mudanças culturais e organizacionais são essenciais. Deveria haver um processo que interrompesse pesquisas não promissoras antes de investir muito dinheiro e emoção nelas. Dias regulares de demonstração e revisões técnicas por pares podem servir como estratégias eficazes para esse propósito. Também é importante encontrar um equilíbrio entre as pessoas que adotam uma ideia nova e brilhante e o encerramento adequado dos projetos atuais.


Mas esta é uma postagem sobre software, então aqui estão algumas práticas para facilitar a ramificação de novos projetos:

  • mantenha o código de avaliação desembaraçado dos algoritmos. As avaliações são normalmente mais estáveis do que as orientações de pesquisa
  • aceite começar um novo projeto do zero, mas tome cuidado com quais componentes são reutilizados. Modularizá-los e limpá-los é um bom investimento
  • em um novo projeto de pesquisa, implemente primeiro o componente mais inovador e arriscado. Isso identifica a maioria dos gargalos que orientam o design futuro de software.

Aumentar a relação sinal-ruído

Bugs e não determinismo podem inviabilizar projetos de pesquisa e tornar resultados inconclusivos

Código barulhento e com erros torna os resultados tão ambíguos e inconclusivos que todo o projeto será uma perda de tempo. Embora você não deva projetar demais as coisas, você pode facilmente seguir estas regras simples para evitar códigos confusos:


  • evite código com efeitos colaterais

  • o padrão é funções em vez de classes; e com classes, prefira encapsulamento versus herança

  • minimizar o comprimento de funções/classes/módulos; minimizar o número de instruções if

  • conheça bem python, mas use técnicas simples. Resista à tentação de entrar nas ervas daninhas intelectuais de metaclasses, decoradores e programação funcional.


É difícil trabalhar com software que produz resultados diferentes durante execuções diferentes. Se você tomou uma decisão importante, mas errada, com base em uma semente infeliz, perderá muito tempo se recuperando. Aqui estão algumas dicas ao lidar com software não determinístico:


  • entender se o ruído vem do algoritmo ou de sua avaliação. As fontes de ruído aumentam e você deve se esforçar para uma avaliação completamente determinística.
  • não pare de encontrar fontes de aleatoriedade até obter um script reproduzível. Lembre-se de que depois de encontrar todas as sementes aleatórias, o ruído pode vir de dados ou de funções genéricas com efeitos colaterais.
  • varie as sementes e determine a variação da linha de base de seus resultados. Não tome decisões com base em resultados não estatisticamente significativos.

Conclusão

A conclusão vem desta postagem sobre código de pesquisa:


“Você não se preocupa com [um bom design de software] porque o código não é o ponto. O código é uma ferramenta que dá a resposta que você precisa” .


É extremamente importante ter ótimas bases de codificação. Mas no final das contas, a exploração e o produto realmente útil é o que importa. Se você usa muito software de produção em pesquisa, perde o tempo necessário para descobrir algo novo. Em vez disso, descubra o que retarda o seu processo de exploração. Acelere os caminhos de pesquisa investindo em ramificações rápidas, tempo para resultados e código limpo e silencioso.


Seria uma loucura argumentar totalmente contra a reutilização de código. Quero apenas salientar que a reutilização de código deve ser uma atividade bem equilibrada. Na pesquisa, a proporção de código descartável é maior do que na produção. A balança inclina-se ainda mais contra a reutilização. Aqui estão mais algumas postagens excelentes com as armadilhas da reutilização de código:


E aqui estão mais algumas postagens defendendo práticas de copypasting:

Obrigado por ler! Acho que algumas partes são um pouco controversas, por favor me avise nos comentários!


Também aparece aqui .