Como resolver um problema antigo usando o ML em Python e .Net


Acontece que algumas tarefas o assombram por muitos anos. Para mim, colar sentenças de textos nas quais a transição para uma nova linha, e muitas vezes também quebra de linha, está rigidamente entupida, tornou-se uma tarefa dessas. Na prática, esse texto é extraído de PDF ou usando OCR. Muitas vezes, era possível encontrar esses textos nos sites das bibliotecas on-line, nos arquivos de documentos antigos que foram editados pelos editores do DOS. E essa formatação interfere na decomposição adequada em frases (e, com hifenizações, em tokens) para o processamento subsequente da PNL. E é banal mostrar esse documento nos resultados de pesquisa - será feio.


Eu resolvi esse problema várias vezes - em Delphi, C #. Depois, foi um algoritmo difícil, onde, com minhas mãos, escrevi, por exemplo, qual poderia ser a largura do texto, para que esse texto fosse considerado formatado "da maneira antiga". Isso nem sempre funcionou perfeitamente, mas, em geral, foi suficiente.


No momento, estou pesquisando alguns projetos de ML em Python. A certa altura, verificou-se que o próximo corpus de documentos consiste em textos extraídos de versões em PDF de artigos científicos. Obviamente, o texto foi extraído com uma quebra de linha rígida pelos caracteres do final do parágrafo, com hífens. Ou seja, era impossível trabalhar normalmente com esses textos ainda mais. Python é atraente porque tem quase tudo! Mas algumas horas de pesquisa não deram nada em ordem (talvez, é claro, era isso que eu estava procurando). E então decidi mais uma vez escrever um pós-processador para esses documentos. A escolha foi entre duas opções - portar seu código anterior com C # ou escrever algo que pudesse ser ensinado. Finalmente, a segunda abordagem foi motivada pelo fato de os textos científicos serem parcialmente exportados de textos de duas colunas e parcialmente de coluna única. Também diferentes tamanhos de fonte. Isso levou ao fato de que a versão antiga, com limites permitidos conectados por fio, geralmente funcionava incorretamente. Para se sentar manualmente novamente, escolha as opções - bem, não, logo a singularidade chegará , não tenho tempo para isso! Então, está decidido - estamos escrevendo uma biblioteca usando o aprendizado de máquina.


Todo o código pode ser encontrado no repositório :



Marcação


Qual é o burburinho e a complexidade do aprendizado de máquina - se o algoritmo falhar em algum lugar, geralmente você não precisa alterar o programa em si. É suficiente coletar novos dados (geralmente eles precisam ser anotados ao mesmo tempo) e reiniciar a construção do modelo. O computador fará o resto por você. Obviamente, há uma chance de que, para novos dados, você tenha que criar novos recursos, alterar a arquitetura, mas na maioria dos casos, acontece que você só precisa verificar se tudo começou a funcionar bem. Isso também é uma dificuldade - coletar e marcar dados pode ser difícil. Ou muito difícil. E também - terrivelmente chato :-)


Então, a coisa mais chata é a marcação. A pasta corpus contém documentos que acabei de extrair do corpo do documento Krapivin2009 com o qual estava trabalhando naquele momento. Existem 10 documentos que me pareciam típicos. Marquei apenas 3, já que no início do treinamento nessa base foi obtida qualidade suficiente do “adesivo”. Se, no futuro, acontecer que tudo não é tão simples, novos documentos com marcação serão descartados nessa pasta e o processo de aprendizado será repetido.


Nesse caso, pareceu-me conveniente que os arquivos permanecessem em texto; portanto, o formato de marcação foi adicionar um sinal no início da linha de que essa linha deveria ser colada à anterior (o caractere '+') ou não (o caractere '*'). Aqui está um trecho (arquivo 1005058.txt ):


*Introduction *Customers on the web are often overwhelmed with options and flooded with promotional messages for +products or services they neither need nor want. When users cannot find what they are searching for, the +e-commerce site struggles to maintain good customer relations. *Employing a recommender system as part of a site's Customer Relationship Management (CRM) activities +can overcome the problems associated with providing users with too little information, or too much of +the wrong information. Recommender systems are able to assist customers during catalog browsing and are +an effective way to cross-sell and improve customer loyalty. *In this paper, we will compare several recommender systems being used as an essential component of +CRM tools under development at Verizon. Our solutions are purposely for the current customers and current +products - recommendations for new customers and new products are out of the scope of this paper. 

Algumas horas de trabalho tedioso e 3 arquivos com 2300 exemplos (uma linha - uma amostra) estão prontos. Isso já é suficiente em muitos casos para classificadores simples, como regressão logística, que foi aplicada.


Funcionalidades


Classificadores não funcionam diretamente com dados de texto. As entradas são servidas com recursos - números ou sinais booleanos (que, novamente, se traduzem nos números 0/1) de que algum recurso está presente ou não. Construir os recursos certos a partir de bons dados é a chave para o sucesso do aprendizado de máquina. Uma característica do nosso caso é que nosso corpus é um texto em inglês. E eu quero ter pelo menos independência mínima de idioma. Pelo menos nas línguas europeias. Portanto, para recursos de texto, usamos um pequeno truque.


A conversão de texto em uma lista de recursos e etiquetas, colando com a linha anterior, é realizada pela função auxiliar _featurize_text_with_annotation :


 x, y = pdf_lines_gluer._featurize_text_with_annotation(raw_text) 

Nota - aqui e mais principalmente fragmentos de código em python go, que você pode ver completamente em um laptop .


Recursos Utilizados:


  • 'this_len' - o comprimento da linha atual em caracteres.
  • 'mean_len' - o comprimento médio das linhas no intervalo -5 ... + 5 linhas.
  • 'prev_len' - o comprimento da linha anterior em caracteres.
  • 'first_chars' - aqui está o nosso recurso complicado. Os 2 primeiros caracteres da string são colocados aqui. Mas, ao mesmo tempo, todas as letras minúsculas (de qualquer alfabeto) são substituídas pelo caractere inglês 'a', maiúsculas são substituídas por 'A', os números são substituídos por '0'. Isso reduz significativamente o número de sinais possíveis, enquanto os resume. Exemplos do que acontece: 'Aa', 'aa', 'AA', '0.', 'a-' ...
  • 'isalpha' - se a letra é o último caractere da linha anterior.
  • 'isdigit' - se o dígito é o último caractere da linha anterior.
  • 'islower' - se a letra minúscula é o último caractere da linha anterior.
  • 'pontuação' - um sinal de pontuação que termina com a linha anterior ou um espaço para outros caracteres.

Um exemplo de um conjunto de recursos para uma linha:


 {'this_len': 12, 'mean_len': 75.0, 'prev_len': 0, 'first_chars': 'Aa', 'isalpha': False, 'isdigit': False, 'islower': False, 'punct': ' '} 

Para que o classificador do pacote sklearn trabalhe com eles, usamos a classe DictVectorizer, com a qual os recursos de string (temos 'first_chars') são convertidos em várias colunas intituladas (nomes podem ser obtidos através de get_feature_names () ) como 'first_chars = Aa ',' first_chars = 0. '. Os recursos booleanos se transformam em zeros e uns, enquanto os valores numéricos permanecem números - os nomes dos campos não são alterados. Externamente, o método retorna numpy.array desse tipo aproximadamente (apenas uma linha é mostrada):


 [[ 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 1. 0. 0. 0. 39.1 30. 0. 1. 36. ]] 

Treinamento do Classificador


Tendo recebido um conjunto de recursos na forma de uma matriz de números de ponto flutuante, agora podemos iniciar o processo de aprendizado. Para fazer isso, usamos regressão logística como um classificador. As classes são desequilibradas, portanto, definimos a opção class_weight = 'equilibrada', verificamos o resultado na parte de teste do caso:


 from sklearn.linear_model import LogisticRegression from sklearn.metrics import classification_report clf = LogisticRegression(random_state=1974, solver='liblinear', max_iter=2000, class_weight='balanced') clf.fit(x_train, y_train) y_pred = clf.predict(x_test) print(classification_report(y_true=y_test, y_pred=y_pred)) 

Temos indicadores de qualidade:


  precision recall f1-score support False 0.82 0.92 0.86 207 True 0.96 0.91 0.94 483 accuracy 0.91 690 macro avg 0.89 0.91 0.90 690 weighted avg 0.92 0.91 0.91 690 

Como você pode ver, em cerca de 1/10 dos casos, temos erros de vários tipos. Mas, na prática, nem tudo é tão assustador. O fato é que, mesmo com a marcação dos olhos, nem sempre é claro onde está o final do parágrafo e onde está o final da frase. Portanto, mesmo a marcação em si pode conter esses erros. Mas os erros mais críticos não são onde ocorrem na borda da proposta, mas onde a proposta permanece rasgada. E existem muito poucos erros desse tipo na realidade.


Recuperar texto


Chegou a hora de restaurar o texto danificado pela extração do PDF. Já podemos determinar se colamos a linha com a anterior, mas há mais um ponto - a hifenização. Tudo é bem direto aqui, então eu codifiquei essa parte com força (eu me permito um pseudocódigo):


      :         :       :      :        \n 

Armado com essa estratégia, restauramos o texto em inglês (existem erros como "ff" e "fi" no original - ele foi simplesmente copiado do caso Krapivin2009):


Texto original em inglês
 text = """The rapid expansion of wireless services such as cellular voice, PCS (Personal Communications Services), mobile data and wireless LANs in recent years is an indication that signicant value is placed on accessibility and portability as key features of telecommunication (Salkintzis and Mathiopoulos (Guest Ed.), 2000). devices have maximum utility when they can be used any- where at anytime". One of the greatest limitations to that goal, how- ever, is nite power supplies. Since batteries provide limited power, a general constraint of wireless communication is the short continuous operation time of mobile terminals. Therefore, power management is y Corresponding Author: Dr. Krishna Sivalingam. Part of the research was supported by Air Force Oce of Scientic Research grants F-49620-97-1- 0471 and F-49620-99-1-0125; by Telcordia Technologies and by Intel. Part of the work was done while the rst author was at Washington State Univer- sity. The authors' can be reached at cej@bbn.com, krishna@eecs.wsu.edu, pagrawal@research.telcordia.com, jcchen@research.telcordia.com c 2001 Kluwer Academic Publishers. Printed in the Netherlands. Jones, Sivalingam, Agrawal and Chen one of the most challenging problems in wireless communication, and recent research has addressed this topic (Bambos, 1998). Examples include a collection of papers available in (Zorzi (Guest Ed.), 1998) and a recent conference tutorial (Srivastava, 2000), both devoted to energy ecient design of wireless networks. Studies show that the signicant consumers of power in a typical laptop are the microprocessor (CPU), liquid crystal display (LCD), hard disk, system memory (DRAM), keyboard/mouse, CDROM drive, oppy drive, I/O subsystem, and the wireless network interface card (Udani and Smith, 1996, Stemm and Katz, 1997). A typical example from a Toshiba 410 CDT mobile computer demonstrates that nearly 36% of power consumed is by the display, 21% by the CPU/memory, 18% by the wireless interface, and 18% by the hard drive. Consequently, energy conservation has been largely considered in the hardware design of the mobile terminal (Chandrakasan and Brodersen, 1995) and in components such as CPU, disks, displays, etc. Signicant additional power savings may result by incorporating low-power strategies into the design of network protocols used for data communication. This paper addresses the incorporation of energy conservation at all layers of the protocol stack for wireless networks. The remainder of this paper is organized as follows. Section 2 introduces the network architectures and wireless protocol stack considered in this paper. Low-power design within the physical layer is brie y discussed in Section 2.3. Sources of power consumption within mobile terminals and general guidelines for reducing the power consumed are presented in Section 3. Section 4 describes work dealing with energy ecient protocols within the MAC layer of wireless networks, and power conserving protocols within the LLC layer are addressed in Section 5. Section 6 discusses power aware protocols within the network layer. Opportunities for saving battery power within the transport layer are discussed in Section 7. Section 8 presents techniques at the OS/middleware and application layers for energy ecient operation. Finally, Section 9 summarizes and concludes the paper. 2. Background This section describes the wireless network architectures considered in this paper. Also, a discussion of the wireless protocol stack is included along with a brief description of each individual protocol layer. The physical layer is further discussed. """ corrected = pdf_lines_gluer._preprocess_pdf(text, clf, v) print(corrected) 

Após a recuperação, obtemos:


Texto em inglês recuperado

A rápida expansão de serviços sem fio, como voz celular, PCS (Personal Communications Services), dados móveis e LANs sem fio nos últimos anos, é uma indicação de que valor significativo é atribuído à acessibilidade e portabilidade como principais recursos das telecomunicações (Salkintzis e Mathiopoulos (Guest Ed .), 2000). os dispositivos têm utilidade máxima quando podem ser usados ​​em qualquer lugar a qualquer momento. "Uma das maiores limitações a esse objetivo, no entanto, são as fontes de alimentação limitadas. Como as baterias fornecem energia limitada, uma restrição geral da comunicação sem fio é o curto tempo de operação contínua dos dispositivos móveis. Portanto, o gerenciamento de energia é fornecido por Autor Correspondente: Dr. Krishna Sivalingam Parte da pesquisa foi apoiada pela Força Aérea Oce, da Scientific Research, concede os subsídios F-49620-97-10471 e F-49620-99-1-0125; por Telcordia Technologies e Intel.Parte do trabalho foi realizada enquanto o primeiro autor estava na Universidade Estadual de Washington.Os autores podem ser encontrados em cej@bbn.com, krishna@eecs.wsu.edu, pagrawal@research.telcordia.com, jcchen@research.telcordia.com c
2001 Editores acadêmicos da Kluwer. Impresso na Holanda.
Jones, Sivalingam, Agrawal e Chen são um dos problemas mais desafiadores da comunicação sem fio, e pesquisas recentes abordaram esse tópico (Bambos, 1998). Os exemplos incluem uma coleção de artigos disponíveis em (Zorzi (Guest Ed.), 1998) e um recente tutorial da conferência (Srivastava, 2000), ambos dedicados ao design eficiente de energia de redes sem fio.
Estudos mostram que os principais consumidores de energia em um laptop típico são o microprocessador (CPU), tela de cristal líquido (LCD), disco rígido, memória do sistema (DRAM), teclado / mouse, unidade de CDROM, unidade de oppy, subsistema de E / S, e a placa de interface de rede sem fio (Udani e Smith, 1996, Stemm e Katz, 1997). Um exemplo típico de um computador móvel Toshiba 410 CDT demonstra que quase 36% da energia consumida é pelo monitor, 21% pela CPU / memória,
18% pela interface sem fio e 18% pelo disco rígido. Consequentemente, a conservação de energia tem sido amplamente considerada no design de hardware do terminal móvel (Chandrakasan e Brodersen, 1995) e em componentes como CPU, discos, displays, etc. Uma economia adicional significativa de energia pode resultar da incorporação de estratégias de baixa energia no design de protocolos de rede usados ​​para comunicação de dados. Este artigo aborda a incorporação da conservação de energia em todas as camadas da pilha de protocolos para redes sem fio.
O restante deste artigo está organizado da seguinte forma. A Seção 2 apresenta as arquiteturas de rede e a pilha de protocolos sem fio considerados neste documento. O design de baixa potência na camada física é brevemente
discutido na Seção 2.3. As fontes de consumo de energia nos terminais móveis e as diretrizes gerais para reduzir a energia consumida são apresentadas na Seção 3. A Seção 4 descreve o trabalho que trata dos protocolos de energia ecológica na camada MAC das redes sem fio, e os protocolos de conservação de energia na camada LLC são abordados na Seção
5. A Seção 6 discute os protocolos com reconhecimento de energia na camada de rede. As oportunidades para economizar energia da bateria na camada de transporte são discutidas na Seção 7. A Seção 8 apresenta técnicas nas camadas OS / middleware e aplicativo para operação com economia de energia.
Finalmente, a Seção 9 resume e conclui o artigo.
2. Antecedentes
Esta seção descreve as arquiteturas de rede sem fio consideradas neste documento. Além disso, é incluída uma discussão sobre a pilha de protocolos sem fio, juntamente com uma breve descrição de cada camada de protocolo individual. A camada física é discutida em mais detalhes.


Há um lugar controverso, mas, em geral, as frases foram restauradas e esse texto já pode ser processado como frases inteiras.


Mas estávamos planejando fazer uma opção independente do idioma. E é exatamente isso que nosso conjunto de recursos visa. Vamos verificar o texto em russo (também um fragmento de texto em PDF):


Texto original em russo
 ru_text = """       -       (. 1.10),    ,   - .       ,      , -   .       ,       .          : 1.        ,       -  (   ,   . 1.10,    ). 2.    ( )           ,     .      ,     .""" corrected = pdf_lines_gluer._preprocess_pdf(ru_text, clf, v) print(corrected) 

Recebido:


Texto em russo restaurado

O método do vetor de suporte é projetado para resolver problemas de classificação, procurando bons limites de decisão (Fig. 1.10) que separam dois conjuntos de pontos que pertencem a categorias diferentes. O limite decisivo pode ser uma linha ou superfície que divide uma amostra de dados de treinamento em espaços pertencentes a duas categorias. Para classificar novos pontos, basta verificar qual lado da fronteira eles estão.
O método do vetor de pesquisa procura esses limites em dois estágios:
1. Os dados são mapeados em um novo espaço de dimensão superior, onde o limite pode ser representado como um hiperplano (se os dados forem bidimensionais, como na Fig. 1.10, o hiperplano degenera em uma linha).
2. Um bom limite de decisão (dividindo o hiperplano) é calculado maximizando a distância do hiperplano até os pontos mais próximos de cada classe; essa etapa é chamada de maximização da diferença. Isso nos permite generalizar a classificação de novas amostras que não pertencem ao conjunto de dados de treinamento.


Tudo é perfeito aqui.


Como usar (geração de código)


No começo, eu tinha um plano para criar um pacote que pudesse ser entregue usando o PIP, mas depois criei uma maneira mais simples (para mim). O conjunto de recursos acabou por não ser muito grande, a própria regressão logística e o DictVectorizer têm uma estrutura simples:


  • Para o DictVectorizer, basta salvar os nomes dos recursos e os campos vocabulário_
  • LogisticRegression possui coef , classes , intercept_

Portanto, outra opção nasceu com a geração de código (no laptop, ela fica na seção "Serializar como código"):


  1. Lemos o arquivo pdf_lines_gluer.py , que contém código auxiliar para vetorizar e restaurar o texto usando um classificador treinado.
  2. No local designado no código-fonte como "# injetar código aqui #", inserimos o código que inicializa o DictVectorizer e LogisticRegression no estado em que entramos no laptop após o treinamento. Também injetamos aqui a única função pública preprocess_pdf (na medida do possível para Python):
     def preprocess_pdf(text: str) -> str: return _preprocess_pdf(text, _clf, _v) 
  3. O código resultante é gravado no arquivo pdf_preprocessor.py

É esse arquivo pdf_preprocessor.py gerado que contém tudo o que precisamos. Para usá-lo, basta pegar este arquivo e soltá-lo em seu projeto. Uso:


 from pdf_preprocessor import preprocess_pdf ... print(preprocess_pdf(text)) 

Se você tiver algum problema em alguns textos, eis o que você precisará fazer:


  1. Coloque seus textos na pasta corpus, anote-os.
  2. Inicie o seu laptop https://github.com/serge-sotnyk/pdf-lines-gluer/blob/master/pdf_gluer.ipynb - levo menos de 5 segundos nos textos atuais.
  3. Faça e teste a nova versão do arquivo pdf_preprocessor.py

Talvez algo dê errado e a qualidade não o satisfaça. Então será um pouco mais complicado - você precisará adicionar novos recursos até encontrar a combinação correta.


C # e ML.NET


Em nossa empresa, a maior parte do código de back-end é baseada em .Net. Obviamente, interagir com Python adiciona inconveniência aqui. E eu gostaria de ter uma solução semelhante em c #. Eu acompanho o desenvolvimento da estrutura do ML.NETmuito tempo . Fiz pequenas tentativas para fazer algo no ano passado, mas elas foram decepcionantes com a cobertura insuficiente de diferentes casos, uma pequena quantidade de documentação e instabilidade da API. Desde a primavera deste ano, a estrutura mudou para um estado de lançamento e eu decidi tentar novamente. Além disso, o trabalho mais tedioso com o layout do corpo já foi realizado.


À primeira vista, a estrutura adicionou conveniência. Mais frequentemente, comecei a encontrar a documentação necessária (embora ainda esteja longe da qualidade e quantidade no sklearn). Mas o mais importante - há um ano, eu ainda não conhecia o sklearn. E agora comecei a ver que muitas coisas no ML.NET estavam tentando fazer à imagem e semelhança (na medida do possível, dada a diferença de plataformas). Essas analogias tornaram mais fácil o aprendizado dos princípios do ML.NET na prática.


Um projeto de trabalho nesta plataforma pode ser visto em https://github.com/serge-sotnyk/pdf-postprocess.cs


Os princípios gerais permaneceram os mesmos - na pasta corpus estão localizados documentos anotados (e não tão). Após o lançamento do projeto ModelCreator, próximo à pasta corpus, veremos a pasta models, onde o arquivo com o modelo treinado será colocado. Essa ainda é a mesma regressão logística com os mesmos recursos.


Mas aqui eu não brinquei mais com a geração de código. Para usar o modelo treinado, use o projeto PdfPostprocessor (que internamente possui o modelo PdfPostprocessModel.zip compilado como um recurso). Depois disso, o modelo pode ser usado, conforme mostrado no exemplo mínimo - https://github.com/serge-sotnyk/pdf-postprocess.cs/blob/master/MinimalUsageExample/Program.cs :


 using PdfPostprocessor; ... static void Main(string[] args) { var postprocessor = new Postprocessor(); Console.WriteLine(); Console.WriteLine("Restored paragraphs in the English text:"); Console.WriteLine(postprocessor.RestoreText(EnText)); Console.WriteLine(); Console.WriteLine("Restored paragraphs in the Russian text:"); Console.WriteLine(postprocessor.RestoreText(RuText)); } 

Enquanto a cópia do modelo da pasta models para o projeto PdfPostprocessor é realizada manualmente - era mais conveniente para mim controlar melhor qual modelo entra no projeto final.


Existe o pacote nuget - PdfPostprocessor. Para usar o pacote e o modelo que você treinou, use o construtor Postprocessor sobrecarregado.


Comparando opções em Python e C #


Embora seja recente na experiência de desenvolvimento em duas plataformas, pode fazer sentido recontá-las brevemente. Eu não sou um defensor militante de uma plataforma específica há muito tempo e sou solidário com os sentimentos dos crentes de várias religiões. Você também precisa entender que ainda trabalho com linguagens com digitação estática a maior parte da minha vida, para que elas fiquem um pouco mais próximas de mim.


O que eu não gostei ao mudar para C #


  • Verbosidade. Ainda assim, o código Python é mais compacto. Esta é a ausência de colchetes do operador e colchetes após if, for. A falta de novas infinitas. Uso ativo de campos, pois são fáceis de transformar em propriedades, se necessário. Mesmo que a privacidade no Python, que é simplesmente indicada por um sublinhado no início do identificador, você se acostume e, na prática, acabou sendo muito conveniente, mais conveniente do que vários modificadores de privacidade em outros idiomas. E a brevidade das construções acelera o desenvolvimento e facilita a leitura do código.
  • Na maioria dos casos, o código Python parece mais limpo e elegante (isso é apenas subjetivo). Isso facilita a leitura e a manutenção.
  • Para o Python, para quase tudo, há algum tipo de função ou decorador em algum tipo de pacote, mas no C # muito tem que ser adicionado. Isso aumenta ainda mais o código com várias funções auxiliares, classes. E isso leva ainda mais tempo.
  • O grau de documentação do C # e suas estruturas é significativamente menor do que no ecossistema Python.
  • A digitação mais rigorosa do ML.NET em comparação com o sklearn onívoro também nos forçou a gastar algum tempo procurando as transformações corretas, e o parágrafo anterior não contribuiu para resolver esse problema.

O que você gostou ao mudar para C #


  • Uma sensação de confiabilidade. Já não com muita frequência, mas com bastante regularidade, o onívoro Python me leva a problemas evasivos. E agora, ao portar o código para C #, ocorreu um erro que tornava alguns recursos inúteis. Após a correção, a precisão aumentou alguns por cento.
  • Velocidade. No código Python, tive que abandonar os recursos vinculados aos quais a decisão de colar foi tomada nas ofertas anteriores - se você enviar propostas ao classificador uma de cada vez, a velocidade geral será menor que o rodapé. Para que o processamento de dados no Python seja rápido, é necessário vetorizá-lo o máximo possível e, às vezes, nos faz abandonar opções potencialmente úteis ou dificulta muito.
  • Linq. Eles são muito mais convenientes do que a compreensão de lista (LC) no Python. Até um LC com um para me obriga a escrever primeiro o que vem depois, depois voltar ao começo e anexar para, e só então escrever uma expressão no início do LC. É só que, nessa ordem, eu acho - a fonte dos registros, itens, para o que converter. LINQ ( "" ) . LC ( for) . , , .
  • Lambda. , . , C# .

— . , .Net , . - — REST C#.


C# . — , - . Microsoft Kotlin — .Net , . Python- — , Julia . .


Conclusão


:


  • , — , , - . , , - .
  • . , ML.NET - . .
  • , Python- .Net. , .

Source: https://habr.com/ru/post/pt457072/


All Articles