
Il arrive que certaines tâches vous hantent depuis de nombreuses années. Pour moi, coller des phrases de textes dans lesquels la transition vers une nouvelle ligne, et souvent aussi le mot wrap, est obstrué de manière rigide, est devenu une telle tâche. En pratique, il s'agit de texte extrait de PDF ou utilisant l'OCR. Souvent, il était possible de trouver de tels textes sur les sites des bibliothèques en ligne, dans les archives d'anciens documents édités par des éditeurs DOS. Et une telle mise en forme interfère alors avec la décomposition appropriée en phrases (et, avec des césures, en jetons) pour le traitement NLP ultérieur. Et il est banal d'afficher un tel document dans les résultats de recherche - ce sera moche.
J'ai résolu ce problème plusieurs fois - dans Delphi, C #. Ensuite, c'était un algorithme difficile, où avec mes mains j'ai écrit, par exemple, quelle pourrait être la largeur du texte, de sorte que ce texte était considéré comme formaté "à l'ancienne". Cela ne fonctionnait pas toujours parfaitement, mais en général, c'était suffisant.
En ce moment, j'explore certains projets ML en Python. À un moment donné, il s'est avéré que le prochain corpus de documents était constitué de textes extraits de versions PDF d'articles scientifiques. Bien sûr, le texte a été extrait avec un saut de ligne dur par les caractères de la fin du paragraphe, avec des tirets. Autrement dit, il était impossible de continuer à travailler normalement avec de tels textes. Python est attrayant car il a presque tout! Mais quelques heures de recherches n'ont rien donné de sain (peut-être, bien sûr, c'est ce que je cherchais). Et puis j'ai décidé une fois de plus d'écrire un post-processeur pour ces documents. Le choix s'est fait entre deux options: porter votre ancien code avec C # ou écrire quelque chose qui pourrait être enseigné. Enfin, la deuxième approche a été motivée par le fait que les textes scientifiques ont été partiellement exportés à partir de textes à deux colonnes et partiellement à partir de textes à une colonne. Différentes tailles de police également. Cela a conduit au fait que l'ancienne version, avec des limites autorisées câblées, fonctionnait souvent de manière incorrecte. Pour vous asseoir à nouveau manuellement, choisissez les options - eh bien non, bientôt la singularité viendra , je n'ai pas le temps pour ça! Donc, c'est décidé - nous écrivons une bibliothèque en utilisant l'apprentissage automatique.
Tout le code se trouve dans le référentiel :
Marquage
Quel est le buzz et la complexité de l'apprentissage automatique - si l'algorithme échoue quelque part, vous n'avez souvent pas besoin de changer le programme lui-même. Il suffit de collecter de nouvelles données (souvent elles doivent être annotées en même temps) et de redémarrer la construction du modèle. L'ordinateur fera le reste pour vous. Bien sûr, il est possible que pour de nouvelles données, vous deviez proposer de nouvelles fonctionnalités, changer l'architecture, mais dans la plupart des cas, il s'avère que vous n'avez qu'à vérifier que tout a commencé à bien fonctionner. C'est également une difficulté - la collecte et le balisage des données peuvent être difficiles. Ou très difficile. Et aussi - terriblement ennuyeux :-)
Donc, la chose la plus ennuyeuse est le balisage. Le dossier corpus contient des documents que je viens de prendre du corps du document Krapivin2009 avec lequel je travaillais à ce moment. Il y a 10 documents qui me paraissent typiques. Je n'en ai marqué que 3, car déjà au début de la formation sur cette base, une qualité suffisante de la «colle» a été obtenue. Si à l'avenir il s'avère que tout n'est pas aussi simple, alors de nouveaux documents avec balisage seront déposés dans ce dossier et le processus d'apprentissage sera répété.
Dans ce cas, il m'a semblé commode que les fichiers restaient du texte, donc le format de balisage était d'ajouter un signe au début de la ligne que cette ligne devait être collée à la précédente (le caractère '+') ou non (le caractère '*'). Voici un extrait (fichier 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.
Quelques heures de travail fastidieux et 3 fichiers avec 2300 exemples (une ligne - un échantillon) sont prêts. Ceci est déjà suffisant dans de nombreux cas pour des classificateurs simples tels que la régression logistique, qui a ensuite été appliquée.
CARACTÉRISTIQUES
Les classificateurs ne fonctionnent pas directement avec les données texte. Les entrées sont servies avec des fonctionnalités - soit des nombres soit des signes booléens (qui, encore une fois, se traduisent par des nombres 0/1) indiquant qu'une fonctionnalité est présente ou non. Construire les bonnes fonctionnalités à partir de bonnes données est la clé du succès de l'apprentissage automatique. Une caractéristique de notre cas est que notre corpus est constitué de textes anglais. Et je veux obtenir au moins une indépendance linguistique minimale. Au moins dans les langues européennes. Par conséquent, pour les fonctionnalités de texte, nous utilisons une petite astuce.
La conversion du texte en une liste d'entités et d'étiquettes, qu'il s'agisse de coller à la ligne précédente, est effectuée par la fonction d'assistance _featurize_text_with_annotation :
x, y = pdf_lines_gluer._featurize_text_with_annotation(raw_text)
Remarque - ici et plus loin, principalement des fragments de code en python go, que vous pouvez voir complètement dans un ordinateur portable .
Caractéristiques utilisées:
- 'this_len' - la longueur de la ligne actuelle en caractères.
- 'mean_len' - la longueur moyenne des lignes dans la plage -5 ... + 5 lignes.
- 'prev_len' - la longueur de la ligne précédente en caractères.
- 'first_chars' - voici notre fonctionnalité délicate. Les 2 premiers caractères de la chaîne sont placés ici. Mais en même temps, toutes les lettres minuscules (de n'importe quel alphabet) sont remplacées par le caractère anglais «a», les lettres majuscules sont remplacées par «A», les chiffres sont remplacés par «0». Cela réduit considérablement le nombre de signes possibles, tout en les résumant. Exemples de ce qui se passe: 'Aa', 'aa', 'AA', '0.', 'a-' ...
- 'isalpha' - si la lettre est le dernier caractère de la ligne précédente.
- 'isdigit' - si le chiffre est le dernier caractère de la ligne précédente.
- 'islower' - si la lettre minuscule est le dernier caractère de la ligne précédente.
- 'punct' - un signe de ponctuation qui se termine par la ligne précédente, ou un espace pour les autres caractères.
Un exemple d'un ensemble de fonctionnalités pour une ligne:
{'this_len': 12, 'mean_len': 75.0, 'prev_len': 0, 'first_chars': 'Aa', 'isalpha': False, 'isdigit': False, 'islower': False, 'punct': ' '}
Pour que le classificateur du package sklearn fonctionne avec eux, nous utilisons la classe DictVectorizer, avec laquelle les fonctionnalités de chaîne (nous avons «first_chars») sont converties en plusieurs colonnes intitulées (les noms peuvent être obtenus via get_feature_names () ) comme «first_chars = Aa ',' first_chars = 0. '. Les fonctionnalités booléennes se transforment en zéros et en uns, tandis que les valeurs numériques restent des nombres - les noms de champ ne changent pas. Extérieurement, la méthode renvoie numpy.array d'environ ce type (une seule ligne est affichée):
[[ 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. ]]
Formation de classificateur
Après avoir reçu un ensemble de fonctionnalités sous la forme d'un tableau de nombres à virgule flottante, nous pouvons maintenant commencer le processus d'apprentissage. Pour ce faire, nous utilisons la régression logistique comme classificateur. Les classes sont déséquilibrées, donc nous définissons l'option class_weight = 'symétrique', nous vérifions le résultat sur la partie test du boîtier:
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))
Nous obtenons de tels indicateurs de qualité:
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
Comme vous pouvez le constater, dans environ 1/10 des cas, nous avons des erreurs de différents types. Mais en pratique, tout n'est pas si effrayant. Le fait est que même avec un balisage oculaire, il n'est pas toujours clair où se trouve la fin du paragraphe et où se trouve juste la fin de la phrase. Par conséquent, même le balisage lui-même peut contenir de telles erreurs. Mais les erreurs les plus critiques ne sont pas là où elles se produisent à la frontière de la proposition, mais là où la proposition reste déchirée. Et il y a très peu de telles erreurs dans la réalité.
Récupérer du texte
Le moment est venu de restaurer le texte endommagé par l'extraction du PDF. Nous pouvons déjà déterminer s'il faut coller la ligne avec la précédente, mais il y a encore un point - la césure. Tout est assez simple ici, donc j'ai codé dur cette partie (je me permets un pseudocode):
: : : : \n
Armé d'une telle stratégie, nous restaurons le texte anglais (il y a des erreurs telles que «ff» et «fi» dans l'original - il a simplement été copié à partir de l'affaire Krapivin2009):
Texte anglais original 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)
Après récupération, nous obtenons:
Texte anglais récupéréL'expansion rapide des services sans fil tels que la téléphonie cellulaire, les services de communication personnels (PCS), les données mobiles et les réseaux locaux sans fil au cours des dernières années est une indication qu'une valeur significative est accordée à l'accessibilité et à la portabilité en tant que caractéristiques clés des télécommunications (Salkintzis et Mathiopoulos (Guest Ed .), 2000). Les appareils ont une utilité maximale lorsqu'ils peuvent être utilisés n'importe où et à tout moment. "L'une des plus grandes limites à cet objectif, cependant, est l'alimentation électrique limitée. Comme les batteries fournissent une puissance limitée, une contrainte générale de la communication sans fil est le court temps de fonctionnement continu du mobile. Par conséquent, la gestion de l'alimentation est y Auteur correspondant: Dr. Krishna Sivalingam. Une partie de la recherche a été financée par l'Air Force Oce of Scientic Research subventions F-49620-97-10471 et F-49620-99-1-0125; par Telcordia Technologies et par Intel. Une partie du travail a été effectuée pendant que le premier auteur était à l'Université de l'État de Washington. Les auteurs peuvent être joints à cej@bbn.com, krishna@eecs.wsu.edu, pagrawal@research.telcordia.com, jcchen@research.telcordia.com c
2001 Kluwer Academic Publishers. Imprimé aux Pays-Bas.
Jones, Sivalingam, Agrawal et Chen sont l'un des problèmes les plus difficiles de la communication sans fil, et des recherches récentes ont abordé ce sujet (Bambos, 1998). Les exemples incluent une collection d'articles disponibles dans (Zorzi (Guest Ed.), 1998) et un récent didacticiel de conférence (Srivastava, 2000), tous deux consacrés à la conception économe en énergie des réseaux sans fil.
Des études montrent que les principaux consommateurs d'énergie dans un ordinateur portable typique sont le microprocesseur (CPU), l'écran à cristaux liquides (LCD), le disque dur, la mémoire système (DRAM), le clavier / la souris, le lecteur de CD-ROM, le lecteur de disquette, le sous-système d'E / S, et la carte d'interface de réseau sans fil (Udani et Smith, 1996, Stemm et Katz, 1997). Un exemple typique d'un ordinateur portable Toshiba 410 CDT montre que près de 36% de l'énergie consommée provient de l'écran, 21% du processeur / de la mémoire,
18% par l'interface sans fil et 18% par le disque dur. Par conséquent, la conservation de l'énergie a été largement prise en compte dans la conception matérielle du terminal mobile (Chandrakasan et Brodersen, 1995) et dans les composants tels que les CPU, les disques, les écrans, etc. Des économies d'énergie supplémentaires importantes peuvent résulter de l'intégration de stratégies de faible consommation dans la conception des protocoles réseau utilisés pour la communication de données. Cet article traite de l'intégration de la conservation de l'énergie à toutes les couches de la pile de protocoles pour les réseaux sans fil.
Le reste de cet article est organisé comme suit. La section 2 présente les architectures de réseau et la pile de protocoles sans fil examinés dans cet article. La conception à faible puissance au sein de la couche physique est brièvement
discuté dans la section 2.3. Les sources de consommation d'énergie dans les terminaux mobiles et les directives générales pour réduire la puissance consommée sont présentées dans la section 3. La section 4 décrit les travaux traitant des protocoles écoénergétiques au sein de la couche MAC des réseaux sans fil, et les protocoles d'économie d'énergie au sein de la couche LLC sont traités dans la section
5. La section 6 traite des protocoles sensibles à l'alimentation dans la couche réseau. Les possibilités d'économiser l'énergie de la batterie au sein de la couche transport sont examinées dans la section 7. La section 8 présente les techniques des couches OS / middleware et d'application pour un fonctionnement économe en énergie.
Enfin, la section 9 résume et conclut le document.
2. Contexte
Cette section décrit les architectures de réseau sans fil considérées dans cet article. En outre, une discussion de la pile de protocoles sans fil est incluse avec une brève description de chaque couche de protocole individuelle. La couche physique est discutée plus en détail.
Il y a un endroit controversé, mais en général, les phrases ont été restaurées et ce texte peut déjà être traité comme des phrases entières.
Mais nous prévoyions de faire une option indépendante de la langue. Et c'est exactement ce à quoi vise notre ensemble de fonctionnalités. Vérifions le texte russe (également un fragment de texte du PDF):
Texte russe d'origine ru_text = """ - (. 1.10), , - . , , - . , . : 1. , - ( , . 1.10, ). 2. ( ) , . , .""" corrected = pdf_lines_gluer._preprocess_pdf(ru_text, clf, v) print(corrected)
Reçu:
Texte russe restauréLa méthode du vecteur de support est conçue pour résoudre les problèmes de classification en recherchant de bonnes limites de décision (Fig. 1.10) qui séparent deux ensembles de points appartenant à des catégories différentes. La frontière décisive peut être une ligne ou une surface divisant un échantillon de données d'entraînement en espaces appartenant à deux catégories. Pour classer de nouveaux points, il suffit de vérifier de quel côté de la frontière ils se trouvent.
La méthode du vecteur de recherche recherche ces limites en deux étapes:
1. Les données sont mappées dans un nouvel espace de dimension supérieure, où la frontière peut être représentée comme un hyperplan (si les données étaient bidimensionnelles, comme sur la figure 1.10, l'hyperplan dégénère en une ligne).
2. Une bonne limite de décision (division de l'hyperplan) est calculée en maximisant la distance de l'hyperplan aux points les plus proches de chaque classe, cette étape est appelée maximisation de l'écart. Cela nous permet de généraliser la classification des nouveaux échantillons qui n'appartiennent pas à l'ensemble de données d'apprentissage.
Tout est parfait ici.
Comment utiliser (génération de code)
Au début, j'avais un plan pour faire un paquet qui peut être livré en utilisant PIP, mais ensuite j'ai trouvé un moyen plus simple (pour moi). L'ensemble des fonctionnalités s'est avéré peu volumineux, la régression logistique elle-même et DictVectorizer ont une structure simple:
- Pour DictVectorizer, il suffit d'enregistrer les noms des fonctionnalités et les champs vocabulary_
- LogisticRegression a coef , classes , intercept_
Par conséquent, une autre option est née avec la génération de code (dans l'ordinateur portable, elle va dans la section "Sérialiser en tant que code"):
- Nous lisons le fichier pdf_lines_gluer.py , qui contient du code auxiliaire pour vectoriser et restaurer du texte à l'aide d'un classificateur qualifié.
- À l'endroit désigné dans le code source comme "# injecter le code ici #", nous insérons le code qui initialise DictVectorizer et LogisticRegression dans l'état que nous avons obtenu dans l'ordinateur portable après la formation. Nous injectons également ici la seule fonction publique (autant que possible pour Python) preprocess_pdf:
def preprocess_pdf(text: str) -> str: return _preprocess_pdf(text, _clf, _v)
- Le code résultant est écrit dans le fichier pdf_preprocessor.py
C'est ce fichier pdf_preprocessor.py généré qui contient tout ce dont nous avons besoin. Pour l'utiliser, prenez simplement ce fichier et déposez-le dans votre projet. Utilisation:
from pdf_preprocessor import preprocess_pdf ... print(preprocess_pdf(text))
Si vous avez des problèmes avec certains textes, voici ce que vous devrez faire:
- Mettez vos textes dans le dossier corpus, annotez-les.
- Lancez votre ordinateur portable https://github.com/serge-sotnyk/pdf-lines-gluer/blob/master/pdf_gluer.ipynb - cela me prend moins de 5 secondes sur les textes actuels.
- Prenez et testez la nouvelle version du fichier pdf_preprocessor.py
Peut-être que quelque chose ira mal et que la qualité ne vous satisfera pas. Ensuite, ce sera un peu plus compliqué - vous devrez ajouter de nouvelles fonctionnalités jusqu'à ce que vous trouviez leur combinaison correcte.
C # et ML.NET
Dans notre entreprise, la plupart du code backend est basé sur .Net. Bien sûr, l'interaction avec Python ajoute ici des inconvénients. Et je voudrais avoir une solution similaire en C #. J'ai suivi le développement du framework ML.NET depuis longtemps . J'ai fait de petites tentatives l'année dernière, mais ils étaient décevants avec la couverture insuffisante de différents cas, une petite quantité de documentation et l'instabilité de l'API. Depuis le printemps de cette année, le framework est passé à l'état de release et j'ai décidé de l'essayer à nouveau. De plus, le travail le plus fastidieux avec la disposition du corps a déjà été fait.
À première vue, le cadre a ajouté la commodité. Plus souvent, j'ai commencé à trouver la documentation nécessaire (bien qu'elle soit encore loin de la qualité et de la quantité dans sklearn). Mais le plus important - il y a un an, je ne savais toujours pas sklearn. Et maintenant, j'ai commencé à voir que beaucoup de choses dans ML.NET essayaient de faire dans l'image et la ressemblance (autant que possible, étant donné la différence de plates-formes). Ces analogies ont facilité l'apprentissage des principes de ML.NET dans la pratique.
Un projet de travail sur cette plate-forme peut être consulté sur https://github.com/serge-sotnyk/pdf-postprocess.cs
Les principes généraux sont restés les mêmes - dans le dossier du corpus se trouvent des documents anotisés (et pas si). Après avoir lancé le projet ModelCreator, à côté du dossier corpus, nous verrons le dossier models, où l'archive avec le modèle formé sera placée. Il s'agit toujours de la même régression logistique avec les mêmes caractéristiques.
Mais ici, je n'ai plus essayé la génération de code. Pour utiliser le modèle formé, prenez le projet PdfPostprocessor (qui a en interne le modèle PdfPostprocessModel.zip compilé en tant que ressource). Après cela, le modèle peut être utilisé, comme indiqué dans l'exemple minimum - 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)); }
Alors que la copie du modèle du dossier models vers le projet PdfPostprocessor est effectuée manuellement - il était plus pratique pour moi de mieux contrôler quel modèle entre dans le projet final.
Il y a nuget-package - PdfPostprocessor. Pour utiliser le package et le modèle que vous avez formés, utilisez le constructeur de postprocesseur surchargé.
Comparaison des options en Python et C #
Bien que issu de l'expérience de développement sur deux plates-formes, il peut être judicieux de les raconter brièvement. Je ne soutiens pas depuis longtemps une plate-forme particulière et je suis sensible aux sentiments des croyants de diverses confessions. Vous devez également comprendre que je travaille toujours avec des langues avec du typage statique pendant la majeure partie de ma vie, donc elles sont juste un peu plus proches de moi.
Ce que je n'ai pas aimé en passant à C #
- Verbosité. Pourtant, le code Python est plus compact. Il s'agit de l'absence de crochets opérateur et de crochets après if, for. L'absence de nouveaux sans fin. Utilisation active des champs car ils sont faciles à transformer en propriétés si nécessaire. Même au fait que la confidentialité en Python, qui est simplement indiquée par un trait de soulignement au début de l'identifiant, vous vous y habituez et dans la pratique, cela s'est avéré très pratique, plus pratique qu'un tas de modificateurs de confidentialité dans d'autres langues. Et la brièveté des constructions accélère le développement et facilite la lecture du code.
- Dans la plupart des cas, le code Python semble plus propre et plus élégant (c'est juste subjectif). Cela facilite la lecture et l'entretien.
- Pour Python, pour presque tout, il y a une sorte de fonction ou de décorateur dans une sorte de paquet, mais en C #, beaucoup doit être ajouté. Cela gonfle encore le code avec diverses fonctions d'assistance, classes. Et cela prend encore plus de temps.
- Le degré de documentation de C # et de ses frameworks est nettement inférieur à celui de l'écosystème Python.
- Le typage plus strict de ML.NET par rapport au sklearn omnivore nous a également obligés à passer un peu de temps à rechercher les bonnes transformations, et le paragraphe précédent n'a pas contribué à résoudre ce problème.
Qu'avez-vous aimé lors du passage à C #
- Un sentiment de fiabilité. Déjà pas très souvent, mais assez régulièrement, le Python omnivore me conduit à des problèmes insaisissables. Et maintenant, lors du portage du code en C #, une erreur a rendu certaines fonctionnalités inutiles. Après correction, la précision a augmenté de quelques pour cent.
- La vitesse. Dans le code Python, j'ai dû abandonner les fonctionnalités liées à la décision de collage prise dans les offres précédentes - si vous soumettez des propositions au classificateur une à la fois, la vitesse globale sera inférieure à la plinthe. Pour que le traitement des données en Python soit rapide, il est nécessaire de le vectoriser autant que possible et parfois cela nous fait abandonner des options potentiellement utiles, ou les rendre très difficiles.
- Linq. Ils sont beaucoup plus pratiques que la List Comprehension (LC) en Python. Même un LC avec un pour moi m'oblige à écrire d'abord ce qui suit, puis à revenir au début et à ajouter, puis à écrire une expression au début du LC. C'est juste que, dans cet ordre, je pense - la source des enregistrements, des éléments, en quoi les convertir. LINQ ( "" ) . LC ( for) . , , .
- Lambda. , . , C# .
— . , .Net , . - — REST C#.
C# . — , - . Microsoft Kotlin — .Net , . Python- — , Julia . .
Conclusion
:
- , — , , - . , , - .
- . , ML.NET - . .
- , Python- .Net. , .