Le bloc-notes Jupyter (iPython) est à juste titre connu comme un bon outil pour prototyper le code et faire toutes sortes de choses d'apprentissage automatique de manière interactive. Mais quand je l'utilise, je rencontre inévitablement les éléments suivants:
Supposons que j'ai développé un pipeline complet d'apprentissage automatique dans jupyter qui comprend la récupération de données brutes à partir de diverses sources, le nettoyage des données, l'ingénierie des fonctionnalités et les modèles de formation après tout. Maintenant, quelle est la meilleure logique pour en faire des scripts avec un code efficace et lisible? Jusqu'à présent, je l'avais abordé de plusieurs manières:
Convertissez simplement .ipynb en .py et, avec seulement de légères modifications, codez en dur tout le pipeline du bloc-notes en un seul script python.
Créez un script unique avec de nombreuses fonctions (environ, 1 fonction pour chaque une ou deux cellules), en essayant de comprendre les étapes du pipeline avec des fonctions distinctes, et nommez-les en conséquence. Spécifiez ensuite tous les paramètres et constantes globales via argparse
.
La même chose que point (2), mais maintenant envelopper toutes les fonctions à l'intérieur de la classe. Maintenant, toutes les constantes globales, ainsi que les sorties de chaque méthode peuvent être stockées en tant qu'attributs de classe.
Convertir un cahier en module python avec plusieurs scripts. Je n'ai pas essayé cela, mais je soupçonne que c'est le plus long moyen de résoudre le problème.
Je suppose que ce paramètre général est très courant chez les scientifiques des données, mais étonnamment, je ne trouve aucun conseil utile.
Mes amis, partagez vos idées et votre expérience. Avez-vous déjà rencontré ce problème? Comment l'avez-vous abordé?
Économiseur de vie : pendant que vous écrivez vos cahiers, refactorisez progressivement votre code en fonctions, en écrivant quelques tests et docstrings
assert
minimes.
Après cela, la refactorisation du cahier au script est naturelle. Non seulement cela, mais cela vous facilite la vie lorsque vous écrivez de longs cahiers, même si vous n'avez pas l'intention de les transformer en autre chose.
Exemple de base du contenu d'une cellule avec des tests et des docstrings "minimaux":
def Zip_count(f):
"""Given Zip filename, returns number of files inside.
str -> int"""
from contextlib import closing
with closing(zipfile.ZipFile(f)) as archive:
num_files = len(archive.infolist())
return num_files
Zip_filename = 'data/myfile.Zip'
# Make sure `myfile` always has three files
assert Zip_count(Zip_filename) == 3
# And total Zip size is under 2 MB
assert os.path.getsize(Zip_filename) / 1024**2 < 2
print(Zip_count(Zip_filename))
Une fois que vous l'avez exporté vers le nu .py
fichiers, votre code ne sera probablement pas encore structuré en classes. Mais cela vaut la peine d'avoir refactorisé votre bloc-notes au point qu'il possède un ensemble de fonctions documentées, chacune avec un ensemble d'instructions assert
simples qui peuvent facilement être déplacées dans tests.py
pour tester avec pytest
, unittest
, ou quoi d'autre. Si cela a du sens, regrouper ces fonctions dans des méthodes pour vos classes est très simple après cela.
Si tout se passe bien, tout ce que vous devez faire après cela est d'écrire votre if __name__ == '__main__':
et ses "hooks": si vous écrivez un script à appeler par le terminal, vous voudrez gérer les arguments de la ligne de commande , si vous écrivez un module, vous ' Je veux penser à son API avec le __init__.py
fichier , etc.
Tout dépend bien sûr du cas d'utilisation prévu: il y a une grande différence entre convertir un ordinateur portable en petit script et le transformer en un module ou un package à part entière.
Voici quelques idées pour un flux de travail bloc-notes vers script :
print
instructions, tracés, etc.if __name__ == '__main__'
.assert
pour chacune de vos fonctions/méthodes et étoffez une suite de tests minimale dans tests.py
.Nous avons le même problème. Cependant, nous utilisons plusieurs cahiers pour prototyper les résultats qui devraient devenir aussi plusieurs scripts python après tout).
Notre approche consiste à mettre de côté le code, qui semble se répéter sur ces blocs-notes. Nous l'avons mis dans le module python, qui est importé par chaque ordinateur portable et également utilisé dans la production. Nous améliorons ce module de manière itérative en continu et ajoutons des tests de ce que nous trouvons pendant le prototypage.
Les carnets deviennent alors un peu comme les scripts de configuration (que nous copions simplement dans la fin des fichiers python) et plusieurs vérifications et validations de prototypage, dont nous n'avons pas besoin dans la production.
Surtout nous n'avons pas peur du refactoring :)
J'ai récemment créé un module ( NotebookScripter ) pour aider à résoudre ce problème. Il vous permet d'appeler un ordinateur portable jupyter via un appel de fonction. C'est aussi simple à utiliser que
from NotebookScripter import run_notebook
run_notebook("./path/to/Notebook.ipynb", some_param="Provided Exteranlly")
Les paramètres des mots clés peuvent être transmis à l'appel de fonction. Il est facile d'adapter un ordinateur portable pour être paramétrable en externe.
Dans une cellule .ipynb
from NotebookScripter import receive_parameter
some_param = receive_parameter(some_param="Return's this value by default when matching keyword not provided by external caller")
print("some_param={0} within the invocation".format(some_param))
run_notebook () prend en charge les fichiers .ipynb ou les fichiers .py - ce qui permet d'utiliser facilement les fichiers .py qui pourraient être générés par nbconvert d'ipython de vscode. Vous pouvez garder votre code organisé d'une manière qui a un sens pour une utilisation interactive, et également le réutiliser/le personnaliser en externe en cas de besoin.
Vous devez décomposer la logique en petites étapes, de cette façon, votre pipeline sera plus facile à entretenir. Puisque vous avez déjà une base de code fonctionnelle, vous voulez garder votre code en cours d'exécution, alors faites de petites modifications, testez et répétez.
J'irais comme ça: