
Sucede que algunas tareas te persiguen durante muchos años. Para mí, pegar juntas oraciones de textos en los que la transición a una nueva línea, y a menudo también la envoltura de la palabra, está rígidamente obstruida, se convirtió en una tarea. En la práctica, este es texto extraído de PDF o usando OCR. A menudo se pueden encontrar dichos textos en los sitios web de las bibliotecas en línea, en archivos de documentos antiguos que fueron editados por editores de DOS. Y ese formato interfiere con el desglose apropiado en oraciones (y, con guiones, en tokens) para el procesamiento posterior de PNL. Y es banal mostrar ese documento en los resultados de búsqueda, será feo.
Resolví este problema varias veces, en Delphi, C #. Luego fue un algoritmo difícil, donde con mis manos escribí, por ejemplo, cuál podría ser el ancho del texto, de modo que este texto se considerara formateado "a la antigua usanza". Esto no siempre funcionó perfectamente, pero en general, fue suficiente.
Por el momento, estoy arrastrándome en algunos proyectos de ML en Python. En un momento dado, resultó que el siguiente corpus de documentos consiste en textos extraídos de versiones en PDF de artículos científicos. Por supuesto, el texto fue extraído con un salto de línea por los caracteres del final del párrafo, con guiones. Es decir, era imposible trabajar más normalmente con dichos textos. ¡Python es atractivo porque lo tiene casi todo! Pero un par de horas de búsquedas no dieron nada cuerdo (quizás, por supuesto, esto es lo que estaba buscando). Y luego decidí una vez más escribir un postprocesador para dichos documentos. La elección fue de dos opciones: transferir su código pasado con C # o escribir algo que se pueda enseñar. Finalmente, el segundo enfoque fue impulsado por el hecho de que los textos científicos se exportaron parcialmente de textos de dos columnas y parcialmente de textos de una sola columna. También diferentes tamaños de fuente. Esto condujo al hecho de que la versión anterior, con límites permisibles cableados, a menudo funcionaba incorrectamente. Para sentarse manualmente de nuevo, elija las opciones: bueno, no, pronto llegará la singularidad , ¡no tengo tiempo para esto! Entonces, está decidido: estamos escribiendo una biblioteca utilizando el aprendizaje automático.
Todo el código se puede encontrar en el repositorio :
Marcado
¿Cuál es el zumbido y la complejidad del aprendizaje automático? Si el algoritmo falla en alguna parte, a menudo no es necesario cambiar el programa en sí. Es suficiente para recopilar datos nuevos (a menudo deben anotarse al mismo tiempo) y reiniciar la construcción del modelo. La computadora hará el resto por usted. Por supuesto, existe la posibilidad de que para nuevos datos tenga que crear nuevas características, cambiar la arquitectura, pero en la mayoría de los casos resulta que solo necesita verificar que todo comenzó a funcionar bien. Esto también es una dificultad: recopilar y marcar datos puede ser difícil. O muy dificil. Y también, terriblemente aburrido :-)
Entonces, lo más aburrido es el marcado. La carpeta del corpus contiene documentos que acabo de tomar del cuerpo del documento Krapivin2009 con el que estaba trabajando en ese momento. Hay 10 documentos que me parecieron típicos. Marqué solo 3, ya que al comienzo del entrenamiento en esta base, se obtuvo suficiente calidad del "adhesivo". Si en el futuro resulta que no todo es tan simple, los nuevos documentos con marcado se colocarán en esta carpeta y se repetirá el proceso de aprendizaje.
En este caso, me pareció conveniente que los archivos siguieran siendo texto, por lo que el formato de marcado consistía en agregar un signo al comienzo de la línea de que esta línea debería estar pegada a la anterior (el carácter '+') o no (el carácter '*'). Aquí hay un fragmento (archivo 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.
Un par de horas de trabajo tedioso y 3 archivos con 2300 ejemplos (una línea - una muestra) están listos. Esto ya es suficiente en muchos casos para clasificadores simples como la regresión logística, que luego se aplicó.
Caracteristicas
Los clasificadores no funcionan directamente con datos de texto. Las entradas se sirven con características, ya sea números o signos booleanos (que, nuevamente, se traducen en números 0/1) de que alguna característica está presente o no. Construir las características correctas a partir de buenos datos es la clave para el éxito del aprendizaje automático. Una característica de nuestro caso es que nuestro corpus son textos en inglés. Y quiero obtener al menos una independencia mínima de idioma. Al menos dentro de las lenguas europeas. Por lo tanto, para las características de texto usamos un pequeño truco.
La función auxiliar _featurize_text_with_annotation convierte la conversión de texto en una lista de características y etiquetas, ya sea para pegar con la línea anterior:
x, y = pdf_lines_gluer._featurize_text_with_annotation(raw_text)
Nota: aquí y más, principalmente fragmentos de código en python go, que puedes ver completamente en una computadora portátil .
Características utilizadas:
- 'this_len': la longitud de la línea actual en caracteres.
- 'mean_len': la longitud promedio de las líneas en el rango -5 ... + 5 líneas.
- 'prev_len': la longitud de la línea anterior en caracteres.
- 'first_chars': aquí está nuestra característica difícil. Los primeros 2 caracteres de la cadena se colocan aquí. Pero al mismo tiempo, todas las letras minúsculas (de cualquier alfabeto) se reemplazan por el carácter inglés 'a', las letras mayúsculas se reemplazan por 'A', los números se reemplazan por '0'. Esto reduce significativamente el número de posibles signos, mientras que los resume. Ejemplos de lo que sucede: 'Aa', 'aa', 'AA', '0.', 'a-' ...
- 'isalpha': si la letra es el último carácter de la línea anterior.
- 'isdigit': si el dígito es el último carácter de la línea anterior.
- 'islower': si la letra minúscula es el último carácter de la línea anterior.
- 'punct': un signo de puntuación que termina con la línea anterior o un espacio para otros caracteres.
Un ejemplo de un conjunto de características para una línea:
{'this_len': 12, 'mean_len': 75.0, 'prev_len': 0, 'first_chars': 'Aa', 'isalpha': False, 'isdigit': False, 'islower': False, 'punct': ' '}
Para que el clasificador del paquete sklearn funcione con ellos, utilizamos la clase DictVectorizer, con la cual las características de cadena (tenemos 'first_chars') se convierten en varias columnas tituladas (los nombres se pueden obtener a través de get_feature_names () ) como 'first_chars = Aa ',' first_chars = 0. '. Las características booleanas se convierten en ceros y unos, mientras que los valores numéricos siguen siendo números; los nombres de campo no cambian. Exteriormente, el método devuelve numpy.array de aproximadamente este tipo (solo se muestra una línea):
[[ 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. ]]
Clasificador de entrenamiento
Habiendo recibido un conjunto de características en forma de una matriz de números de coma flotante, ahora podemos comenzar el proceso de aprendizaje. Para hacer esto, utilizamos la regresión logística como clasificador. Las clases no están balanceadas, por lo tanto, configuramos la opción class_weight = 'balanceado', verificamos el resultado en la parte de prueba del 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))
Obtenemos tales indicadores de calidad:
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 puede ver, en aproximadamente 1/10 de los casos, tenemos errores de varios tipos. Pero en la práctica, no todo es tan aterrador. El hecho es que, incluso con el marcado del ojo, no siempre está claro dónde está el final del párrafo y dónde está el final de la oración. Por lo tanto, incluso el marcado en sí mismo puede contener tales errores. Pero los errores más críticos no están donde ocurren en el borde de la propuesta, sino donde la propuesta permanece desgarrada. Y hay muy pocos errores de este tipo en la realidad.
Recuperar texto
Ha llegado el momento de restaurar el texto dañado por la extracción de PDF. Ya podemos determinar si pegar la línea con la anterior, pero hay un punto más: la separación silábica. Aquí todo es bastante sencillo, así que codifiqué esta parte con fuerza (me permito un pseudocódigo):
: : : : \n
Armados con tal estrategia, restauramos el texto en inglés (hay errores como "ff" y "fi" en el original; simplemente se copió del caso Krapivin2009):
Texto original en 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)
Después de la recuperación, obtenemos:
Texto en inglés recuperadoLa rápida expansión de los servicios inalámbricos como voz celular, PCS (Servicios de comunicaciones personales), datos móviles y LAN inalámbricas en los últimos años es una indicación de que se le da un valor significativo a la accesibilidad y portabilidad como características clave de las telecomunicaciones (Salkintzis y Mathiopoulos (Guest Ed .), 2000). los dispositivos tienen la máxima utilidad cuando se pueden usar en cualquier lugar y en cualquier momento ". Sin embargo, una de las mayores limitaciones para ese objetivo son las fuentes de alimentación finas. Dado que las baterías proporcionan una potencia limitada, una limitación general de la comunicación inalámbrica es el corto tiempo de operación continua del móvil terminales. Por lo tanto, la administración de energía es su autor de correspondencia: Dr. Krishna Sivalingam. Parte de la investigación fue apoyada por la Fuerza Aérea Oce de Investigaciones Científicas subvenciones F-49620-97-10471 y F-49620-99-1-0125; por Telcordia Tecnologías e Intel. Parte del trabajo se realizó mientras el primer autor estaba en la Universidad Estatal de Washington. Se puede contactar con los autores en cej@bbn.com, krishna@eecs.wsu.edu, pagrawal@research.telcordia.com, jcchen@research.telcordia.com c
2001 Kluwer Academic Publishers. Impreso en los Países Bajos.
Jones, Sivalingam, Agrawal y Chen, uno de los problemas más desafiantes en la comunicación inalámbrica, y la investigación reciente ha abordado este tema (Bambos, 1998). Los ejemplos incluyen una colección de documentos disponibles en (Zorzi (Guest Ed.), 1998) y un tutorial de conferencia reciente (Srivastava, 2000), ambos dedicados al diseño de redes inalámbricas energéticamente eficientes.
Los estudios demuestran que los consumidores importantes de energía en una computadora portátil típica son el microprocesador (CPU), la pantalla de cristal líquido (LCD), el disco duro, la memoria del sistema (DRAM), el teclado / mouse, la unidad de CDROM, la unidad de disco, el subsistema de E / S, y la tarjeta de interfaz de red inalámbrica (Udani y Smith, 1996, Stemm y Katz, 1997). Un ejemplo típico de una computadora móvil Toshiba 410 CDT demuestra que casi el 36% de la energía consumida es por la pantalla, el 21% por la CPU / memoria,
18% por la interfaz inalámbrica y 18% por el disco duro. En consecuencia, la conservación de energía se ha considerado en gran medida en el diseño de hardware del terminal móvil (Chandrakasan y Brodersen, 1995) y en componentes como CPU, discos, pantallas, etc. Se pueden obtener ahorros de energía adicionales significativos al incorporar estrategias de baja potencia en el diseño de protocolos de red utilizados para la comunicación de datos. Este documento aborda la incorporación de la conservación de energía en todas las capas de la pila de protocolos para redes inalámbricas.
El resto de este documento está organizado de la siguiente manera. La Sección 2 presenta las arquitecturas de red y la pila de protocolos inalámbricos considerados en este documento. El diseño de baja potencia dentro de la capa física es breve
discutido en la Sección 2.3. Las fuentes de consumo de energía dentro de los terminales móviles y las pautas generales para reducir la energía consumida se presentan en la Sección 3. La Sección 4 describe el trabajo relacionado con los protocolos de energía eficiente dentro de la capa MAC de las redes inalámbricas, y los protocolos de conservación de energía dentro de la capa LLC se abordan en la Sección
5. La Sección 6 discute los protocolos de alimentación dentro de la capa de red. Las oportunidades para ahorrar energía de la batería dentro de la capa de transporte se discuten en la Sección 7. La Sección 8 presenta técnicas en el sistema operativo / middleware y las capas de aplicación para un funcionamiento con eficiencia energética.
Finalmente, la Sección 9 resume y concluye el documento.
2. Antecedentes
Esta sección describe las arquitecturas de red inalámbrica consideradas en este documento. Además, se incluye una discusión sobre la pila de protocolos inalámbricos junto con una breve descripción de cada capa de protocolo individual. La capa física se discute más a fondo.
Hay un lugar controvertido, pero en general, las oraciones se han restaurado y dicho texto ya puede procesarse como oraciones completas.
Pero estábamos planeando hacer una opción independiente del idioma. Y esto es exactamente a lo que apunta nuestro conjunto de características. Revisemos el texto en ruso (también un fragmento de texto del PDF):
Texto original en ruso ru_text = """ - (. 1.10), , - . , , - . , . : 1. , - ( , . 1.10, ). 2. ( ) , . , .""" corrected = pdf_lines_gluer._preprocess_pdf(ru_text, clf, v) print(corrected)
Recibido:
Texto ruso restauradoEl método de vector de soporte está diseñado para resolver problemas de clasificación mediante la búsqueda de buenos límites de decisión (Fig. 1.10) que separan dos conjuntos de puntos que pertenecen a diferentes categorías. El límite decisivo puede ser una línea o superficie que divide una muestra de datos de entrenamiento en espacios que pertenecen a dos categorías. Para clasificar nuevos puntos, es suficiente solo verificar de qué lado del borde están.
El método de vector de búsqueda busca tales límites en dos etapas:
1. Los datos se asignan a un nuevo espacio de dimensión superior, donde el límite se puede representar como un hiperplano (si los datos fueran bidimensionales, como en la figura 1.10, el hiperplano se degenera en una línea).
2. Un buen límite de decisión (dividiendo el hiperplano) se calcula maximizando la distancia desde el hiperplano a los puntos más cercanos de cada clase, este paso se llama maximizar la brecha. Esto nos permite generalizar la clasificación de nuevas muestras que no pertenecen al conjunto de datos de entrenamiento.
Todo es perfecto aquí.
Cómo usar (generación de código)
Al principio, tenía un plan para hacer un paquete que se pueda entregar usando PIP, pero luego se me ocurrió una forma más simple (para mí). El conjunto de características resultó no ser muy grande, la regresión logística en sí y DictVectorizer tienen una estructura simple:
- Para DictVectorizer, es suficiente guardar los nombres de las funciones y los campos de vocabulario_
- LogisticRegression tiene coef , clases , intercepción_
Por lo tanto, otra opción nació con la generación de código (en la computadora portátil, va en la sección "Serializar como código"):
- Leemos el archivo pdf_lines_gluer.py , que contiene código auxiliar para vectorizar y restaurar texto usando un clasificador entrenado.
- En el lugar designado en la fuente como "# inyectar código aquí #", insertamos el código que inicializa el DictVectorizer y LogisticRegression en el estado que obtuvimos en nuestra computadora portátil después del entrenamiento. También inyectamos aquí la única función preprocess_pdf pública (en la medida de lo posible para Python):
def preprocess_pdf(text: str) -> str: return _preprocess_pdf(text, _clf, _v)
- El código resultante se escribe en el archivo pdf_preprocessor.py
Es este archivo pdf_preprocessor.py generado que contiene todo lo que necesitamos. Para usarlo, simplemente tome este archivo y colóquelo en su proyecto. Uso:
from pdf_preprocessor import preprocess_pdf ... print(preprocess_pdf(text))
Si tiene algún problema con algunos textos, esto es lo que deberá hacer:
- Ponga sus textos en la carpeta del corpus, anótelos.
- Inicie su computadora portátil https://github.com/serge-sotnyk/pdf-lines-gluer/blob/master/pdf_gluer.ipynb : me toma menos de 5 segundos en los textos actuales.
- Tome y pruebe la nueva versión del archivo pdf_preprocessor.py
Quizás algo salga mal y la calidad no lo satisfaga. Entonces será un poco más complicado: necesitará agregar nuevas funciones hasta que encuentre su combinación correcta.
C # y ML.NET
En nuestra empresa, la mayor parte del código de back-end se basa en .Net. Por supuesto, interactuar con Python agrega inconvenientes aquí. Y me gustaría tener una solución similar en C #. He estado siguiendo el desarrollo del marco ML.NET durante mucho tiempo . Hice pequeños intentos de hacer algo el año pasado, pero fueron decepcionantes con la cobertura insuficiente de diferentes casos, una pequeña cantidad de documentación y la inestabilidad de la API. Desde la primavera de este año, el marco ha cambiado a un estado de lanzamiento y decidí intentarlo nuevamente. Además, el trabajo más tedioso con el diseño del cuerpo ya se ha realizado.
A primera vista, el marco agregó conveniencia. Más a menudo comencé a encontrar la documentación necesaria (aunque todavía está lejos de la calidad y la cantidad en sklearn). Pero lo más importante: hace un año todavía no sabía sklearn. Y ahora comencé a ver que muchas cosas en ML.NET intentaban hacer en la imagen y la semejanza (en la medida de lo posible, dada la diferencia en las plataformas). Estas analogías han facilitado el aprendizaje de los principios de ML.NET en la práctica.
Se puede ver un proyecto de trabajo en esta plataforma en https://github.com/serge-sotnyk/pdf-postprocess.cs
Los principios generales siguieron siendo los mismos: en la carpeta del corpus se encuentran documentos anotizados (y no tan). Después de lanzar el proyecto ModelCreator, junto a la carpeta del corpus, veremos la carpeta de modelos, donde se colocará el archivo con el modelo entrenado. Esta sigue siendo la misma regresión logística con las mismas características.
Pero aquí ya no incursioné en la generación de código. Para usar el modelo entrenado, tome el proyecto PdfPostprocessor (que internamente tiene el modelo PdfPostprocessModel.zip compilado como recurso). Después de eso, se puede usar el modelo, como se muestra en el ejemplo 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)); }
Si bien la copia del modelo de la carpeta de modelos al proyecto PdfPostprocessor se realiza manualmente, fue más conveniente para mí controlar mejor qué modelo ingresa al proyecto final.
Hay nuget-package - PdfPostprocessor. Para usar el paquete y el modelo que entrenó, use el constructor de Postprocesador sobrecargado.
Comparación de opciones en Python y C #
Si bien la experiencia de desarrollo en dos plataformas es reciente, puede tener sentido volver a contarlas brevemente. No he sido un partidario militante de una plataforma en particular durante mucho tiempo y simpatizo con los sentimientos de los creyentes de diversas religiones. También debe comprender que todavía trabajo con idiomas con escritura estática durante la mayor parte de mi vida, por lo que están un poco más cerca de mí.
Lo que no me gustó al cambiar a C #
- Verbosidad Aún así, el código de Python es más compacto. Esta es la ausencia de paréntesis de operador y paréntesis después de if, for. La falta de novedades sin fin. Uso activo de campos, ya que son fáciles de convertir en propiedades si es necesario. Incluso al hecho de que la privacidad en Python, que simplemente se indica con un guión bajo al comienzo del identificador, te acostumbras y, en la práctica, resultó ser muy conveniente, más conveniente que un montón de modificadores de privacidad en otros idiomas. Y la brevedad de las construcciones acelera el desarrollo y facilita la lectura del código.
- En la mayoría de los casos, el código Python se ve más limpio y elegante (esto es solo subjetivo). Esto hace que sea más fácil de leer y mantener.
- Para Python, para casi todo, hay algún tipo de función o decorador en algún tipo de paquete, pero en C # hay que agregar mucho. Esto infla aún más el código con varias funciones auxiliares, clases. Y lleva aún más tiempo.
- El grado de documentación de C # y sus marcos es significativamente menor que en el ecosistema de Python.
- La tipificación más estricta de ML.NET en comparación con el omnívoro sklearn también nos obligó a pasar un tiempo buscando las transformaciones correctas, y el párrafo anterior no contribuyó a resolver este problema.
¿Qué te gustó al cambiar a C #?
- Una sensación de fiabilidad. Ya no muy a menudo, pero con bastante frecuencia, el omnívoro Python me lleva a problemas evasivos. Y ahora, al portar el código a C #, hubo un error que hizo que algunas características fueran inútiles. Después de la corrección, la precisión aumentó en un par de por ciento.
- Velocidad. En el código de Python, tuve que abandonar las funciones que estaban vinculadas a la decisión de pegado que se tomó en ofertas anteriores: si envía propuestas al clasificador de una en una, la velocidad general será menor que la placa base. Para que el procesamiento de datos en Python sea rápido, es necesario vectorizarlo tanto como sea posible y, a veces, nos hace rechazar opciones potencialmente útiles, o es muy difícil hacerlas.
- Linq. Son mucho más convenientes que la Lista de comprensión (LC) en Python. Incluso un LC con uno para forzarme a escribir primero lo que sigue, luego volver al principio y agregar, y solo entonces escribir una expresión al comienzo del LC. Es solo que, en ese orden, creo: la fuente de registros, elementos, a qué convertir. LINQ ( "" ) . LC ( for) . , , .
- Lambda. , . , C# .
— . , .Net , . - — REST C#.
C# . — , - . Microsoft Kotlin — .Net , . Python- — , Julia . .
Conclusión
:
- , — , , - . , , - .
- . , ML.NET - . .
- , Python- .Net. , .