Dans cet article, j'ai résumé les points, les astuces et le savoir-faire dont les data scientists et les ingénieurs en IA doivent être conscients lors de la mise en œuvre de programmes en Python.
● 16 mai 2020: ajout Livre: Programmeur automoteur auteur: M. Shimizukawa a fourni un supplément détaillé à cet article et des réponses à mes questions. Ce lien vous mènera au commentaire.
Le livre a été publié en avril 2020.
[Livre] [Introduction à l'apprentissage automatique pour ceux qui visent à devenir des ingénieurs en IA. Apprenez le flux des algorithmes lors de la mise en œuvre (Dentsu International Information Service Takuya Shimizu, Yutaro Ogawa, Technical Review)](https://www.amazon.co.jp/ dp / 4297112094 /) https://www.amazon.co.jp/dp/4297112094/
Ce message n'a pas pu être écrit dans le livre ci-dessus ** "Résumé des éléments dont les ingénieurs IA doivent être conscients lors de la mise en œuvre de programmes en Python" ** est.
Le contenu de cet article est juste le point dont l'auteur est conscient. Ce n'est pas la seule réponse, mais nous espérons que vous la trouverez utile.
Tout d'abord, le contenu de cet article est affiché.
Après cela, l'explication de chaque contenu sera décrite.
Niveau 1 1.1 Respectez les conventions de dénomination des variables, fonctions, classes et méthodes 1.2 Méthode de dénomination 1: supprimer les parties redondantes des noms de variables et des noms de méthodes 1.3 La description de l'importation suit les règles 1.4 Graine fixe d'un nombre aléatoire pour assurer la reproductibilité 1.5 Le programme est exécuté en tant que fonction
Niveau 2 2.1 Méthode de dénomination 2: Nommer avec la notation inverse pour faciliter la lecture 2.2 Connaître S dans SOLID, raccourcir les fonctions et méthodes avec une seule responsabilité 2.3 Ajouter des indices de type aux fonctions et méthodes 2.4 docstring pour les classes, méthodes et fonctions 2.5 Lors de l'enregistrement d'un modèle entraîné, enregistrez ensemble les informations telles que le prétraitement et les hyperparamètres
Niveau 3
3.1 Méthode de dénomination 3: Donnez un nom qui comprend vos responsabilités avec des mots et des parties anglais appropriés.
3.2 Mettre en œuvre la gestion des exceptions de manière appropriée
3.3 Mettre en œuvre correctement les journaux
3.4 Rendre le nombre d'arguments de fonction et de méthode de 3 ou moins
3.5 Utilisez correctement * args
et ** kwargs
Niveau 4 4.1 Déclaration courte if avec opérateur ternaire 4.2 Implémenter le prétraitement et les classes de modèle dans la conformité sklearn 4.3 Faire bon usage des décorateurs 4.4 Unifier les paramètres de l'éditeur pour le développement d'équipe 4.5 Préparer un modèle pour la pull request de GitHub et décrire les notes
Comment nommer les fonctions et les variables est un problème. La méthode de dénomination est également importante, mais d'abord, suivez les règles de dénomination en règle générale. (La convention de dénomination est introduite sous la forme "PEP8: Python Code Style Guide")
● Variables, fonctions, méthodes, modules
⇒Séparer les mots avec des traits de soulignement si nécessaire
Exemple lower_case_with_underscores
● Nom de la classe
⇒Connectez les mots supérieurs uniquement au début, n'utilisez pas de trait de soulignement
Exemple CapWords
● Variables privées utilisées uniquement dans la classe
⇒ Souligner avant le nom de la variable
Exemple _single_leading_underscores
"Méthodes privées utilisées uniquement dans la classe"
⇒ Souligner avant le nom de la méthode
Exemple _single_leading_underscore (self, ...)
● Constante ⇒ Seuls les mots en majuscules, séparer les mots avec un trait de soulignement Exemple ʻALL_CAPS_WITH_UNDERSCORES`
● Nom du package
⇒ Uniquement en minuscules
Exemple abaisse
** * Remarque 1 **: Différence entre fonction et méthode Les fonctions sont des procédures indépendantes qui ne font pas partie de la classe. La méthode pointe vers une fonction dans la classe.
** * Remarque 2 **: Module et package Le package est le plus grand niveau supérieur. Les modules sont des fichiers dans des packages. Par exemple, sklearn est un package. linear_model dans sklearn.linear_model est un module.
Par exemple, si la classe class_1 a la variable max_length, le nom de la variable membre est
Ne le définissez pas sur class_1_max_length
, mais réglez-le simplement sur max_length
.
Parce que lors de l'accès à cette variable de classe depuis une autre classe
class_1.class_1_max_length = 10
C'est parce que le nom de la classe devient redondant.
class_1.max_length = 10
Il vaut mieux être.
Les variables membres sont nommées en imaginant qu'elles seront "nom de classe.nom de variable" lorsqu'elles sont utilisées.
Voici trois choses à garder à l'esprit lors de l'importation de classes et de fonctions externes.
● Ordre d'écriture de l'importation Décrivez les trois types de bibliothèques séparés par des lignes vides, comme indiqué ci-dessous.
importer une bibliothèque standard
Ligne blanche
importation liée à un tiers (installation pip depuis PyPI)
Ligne blanche
importer ce que nous avons créé pour cette fois`
Ligne blanche
● Comment décrire l'importation Importez le module entier. Par exemple, si vous avez la classe class_1 dans le module module_1 du package pkg
from pkg.module_1 import class_1
ne pas
from pkg import module_1
Comme
Dans le programme, par exemple
my_class = module_1.class_1()
Et utilisez-le au niveau du module.
● Comment décrire l'importation 2 N'importez pas plusieurs packages avec l'importation.
import pkg, pkg2
ne pas
import pkg import pkg2
Il est décrit comme.
Cependant, plusieurs descriptions de modules sont acceptables. Par exemple:
from pkg import module_1, module_2
** * Remarques ** J'ai également une méthode d'importation assez appropriée. Ce n'est pas bon. L'autoformateur et d'autres outils corrigeront la description des instructions d'importation, il est donc également recommandé de les utiliser.
Comme il existe de nombreuses parties aléatoires lors de la mise en œuvre de la science des données et de l'IA, la graine du nombre aléatoire est toujours fixée pour assurer la reproductibilité du programme.
L'exemple de mise en œuvre est le suivant.
import os
import random
import numpy as np
import torch
SEED_VALUE = 1234 #Cela peut être n'importe quoi
os.environ['PYTHONHASHSEED'] = str(SEED_VALUE)
random.seed(SEED_VALUE)
np.random.seed(SEED_VALUE)
torch.manual_seed(SEED_VALUE) #Lors de l'utilisation de PyTorch
Cependant, si vous utilisez GPU avec PyTorch,
torch.backends.cudnn.deterministic = True
torch.backends.cudnn.benchmark = False
Mettre en place. S'ils ne sont pas définis, la reproductibilité ne peut pas être garantie lors de l'utilisation du GPU.
Cependant, torch.backends.cudnn.deterministic = True
ralentit le calcul sur le GPU.
Par conséquent, dans mon cas, je donne la priorité à la vitesse d'exécution et ne demande pas de garantie de reproductibilité des apprentissages sur GPU.
Cliquez ici pour plus de détails sur la procédure permettant d'assurer la reproductibilité avec PyTorch. PyTorch REPRODUCIBILITY
De plus, dans le cas de scicit-learn, il y a une partie qui reçoit une graine aléatoire dans chaque algorithme, donc corrigez cela également.
Exemple: scikit-learn
from sklearn.linear_model import LogisticRegression
SEED_VALUE = 1234 #Cela peut être n'importe quoi
clf = LogisticRegression(random_state=SEED_VALUE)
Les programmes non fonctionnalisés s'exécuteront plus lentement, que ce soit dans Jupyter Notebook ou l'exécution de la ligne de commande Python.
Par exemple, chez Google Colaboratory
import time
import numpy as np
start = time.time()
data = np.random.rand(5000)
sum = 0
for i in range(len(data)):
for j in range(len(data)):
sum += data[j]
elapsed_time = time.time() - start
print(elapsed_time)
Il a fallu 8,4 secondes pour fonctionner.
Il s'agit de l'état dans lequel le programme est écrit et exécuté dans une cellule.
Il est exécuté dans une cellule de la même manière, mais exécutons-le comme une fonction autour de l'instruction principale for.
import time
import numpy as np
def main():
data = np.random.rand(5000)
sum = 0
for i in range(len(data)):
for j in range(len(data)):
sum += data[j]
return 0
start = time.time()
main()
elapsed_time = time.time() - start
print(elapsed_time)
Le résultat est de 6,2 secondes. Ce qui a pris 8 secondes est maintenant de 6 secondes.
Même si vous l'exécutez dans Jupyter Notebook comme ceci, au lieu d'exécuter le traitement qui prend beaucoup de temps en écriture solide, faites-en une fonction comme main () et exécutez cette fonction.
La même chose est vraie lors de l'implémentation d'un fichier python qui s'exécute à partir de la ligne de commande, par exemple hogehoge.py.
python hogehoge.py
Hogehoge.py est écrit comme suit afin qu'il ne soit pas lent lorsqu'il est exécuté avec.
#système d'importation
import fuga
#Fonctions et classes utilisées dans la fonction principale
def piyo():
your code
#fonction principale
def main():
your code
if __name__ == "__main__":
main()
À la fin, ʻif name == "main": , l'exécution de
main () ʻest incluse dans l'instruction if.
import hogehoge
C'est pour empêcher cette fonction main () d'être exécutée lorsque vous le faites.
Sans cette instruction if, main () serait exécutée simplement en important.
Par exemple, si vous souhaitez créer trois types de variables, telles que la longueur de a, la longueur de b et la longueur de c,
a_length b_length c_length
Sans pour autant
length_a length_b length_c
ça ira.
Cette notation est appelée notation inversée.
Dans le cas de la notation inversée, les débuts des mots sont les mêmes, ce qui facilite la lecture du programme.
De plus, si vous vous concentrez sur une au lieu de la longueur
a_length a_width a_max_length
Ce sera écrit comme ça.
Mon sentiment est: "Écrivez l'objet sur lequel vous vous concentrez au début et unifiez le début des variables."
Livres [Mystery of Agile Software Development, Clean Code, Clean Coder, Clean Architecture, etc.](https://www.amazon.co.jp/%E6%9C%AC-%E3%83%AD%E3%83% 90% E3% 83% BC% E3% 83% 88% E3% 83% BBC% E3% 83% BB% E3% 83% 9E% E3% 83% BC% E3% 83% 81% E3% 83% B3 / s? rh = n% 3A465392% 2Cp_27% 3A% E3% 83% AD% E3% 83% 90% E3% 83% BC% E3% 83% 88% E3% 83% BBC% E3% 83% BB% E3% 83% 9E% E3% 83% BC% E3% 83% 81% E3% 83% B3) Auteur de Robert C. Martin (l'un des membres de la Déclaration de développement logiciel agile) Le principe de conception de logiciel préconisé par SOLID est.
Cependant, au niveau de cet article, vous n'avez pas besoin d'être au courant de tout SOLID.
Cependant, soyez bien conscient du premier SOLID, ** S: principe de responsabilité unique **.
Une seule responsabilité signifie que «les fonctions, classes et méthodes ne doivent remplir qu'une seule responsabilité».
La définition de la taille indiquée par ** "unique" ** ici, l'équilibre entre abstraction et concret est difficile, ** Le fait est que les fonctions, classes et méthodes sont aussi courtes que possible et ne sont pas affectées par les changements dans les concepts de niveau supérieur **.
Le travail réalisé par les data scientists et les ingénieurs IA est C'est un contenu très procédural et semblable à un flux tel que "le prétraitement des données, l'apprentissage, le raisonnement, ...".
Ensuite, le programme à implémenter devient procédural, et une classe ou méthode principale devient gonflée. Il est facile de finir par faire beaucoup de choses (avec de nombreuses responsabilités) de haut en bas dans une seule classe ou méthode.
C'est bien s'il est fermé avec Jupyter Notebook, mais cet état est difficile à introduire la science des données et l'IA dans le développement du système.
Développement de SoE (System of Engagement) comme un système d'IA Il existe de nombreux développements agiles, pas des développements en cascade tels que SoR (System of Records), où la définition des exigences et la conception externe / interne se font exactement.
Dans le développement agile, il est fondamental de réaliser un test automatique CI (Continuous Integration) ≒. Et parce qu'il est agile, nous le ferons réellement, verrons comment cela fonctionne au niveau du prototype, trouverons les changements Kaizen et viserons quelque chose de mieux.
Au moment de ce Kaizen, si la responsabilité d'une seule classe ou méthode est énorme, le nombre de lignes de code à recréer augmentera.
Plus vous recréez de lignes de code, plus l'impact est large.
Ensuite, il y aura de nombreux tests unitaires qui doivent être nouvellement créés.
Dans le même temps, de nombreux tests unitaires que nous avons créés seront jetés.
** Si vous procédez au développement dans une situation où de tels remplacements majeurs de tests unitaires se produisent fréquemment, les tests unitaires ne seront pas écrits correctement et la qualité du système se détériorera. ** **
De plus, la refonte par Kaizen affectera les endroits inattendus et facilitera l'apparition de bogues.
Les scientifiques des données et les ingénieurs en IA souhaitent également adopter activement Kaizen dans le développement agile. Les fonctions, classes et méthodes à implémenter doivent être courtes et avoir une seule responsabilité. Nous garderons à l'esprit ** une implémentation résistante aux changements Kaizen **.
Plutôt que d'avoir une longue fonction, classe, méthode ** Est-ce trop court? Il est souhaitable qu'il y ait autant de fonctions, classes et méthodes qui vous inquiètent **.
Selon le livre ou l'article, on peut dire que le nombre de lignes pour une fonction ou méthode doit être dans les 5 lignes. Je pense que l'utilisation de 5 lignes comme standard est trop courte et trop sévère pour les implémentations basées sur l'IA, ce qui est contre-productif.
Au lieu de la norme du nombre de lignes, nous essayons d'avoir une longueur qui remplit une seule responsabilité, une implémentation qui a une plage d'influence étroite lors du changement de Kaizen, et une implémentation qui rejette rarement les tests unitaires.
Une astuce pour cela consiste à commenter au début de la fonction, de la classe ou de la méthode. "" "Cette méthode est responsable de la mise en œuvre ●●." ""
Dans un premier temps, il est également recommandé d'écrire explicitement vos responsabilités.
Si vous divisez une fonction ou une méthode en pensant à "SOLID S: responsabilité unique", vous pouvez créer de nombreuses fonctions et méthodes.
Il est difficile de comprendre s'il existe de nombreuses fonctions et méthodes. Ce n'est pas grave lors de l'écriture de code, mais si vous révisez ce code après 3 mois ou que quelqu'un d'autre essaie de l'utiliser
"Qu'y a-t-il dans l'argument de cette fonction et quelle est la sortie?"
Et soyez confus.
Par conséquent, lors de l'implémentation d'une fonction, ajoutez un indice de type. Les conseils de type sont écrits comme suit.
def calc_billing_amount(amount: int, price: int) -> int:
billing_amount = amount*price
return billing_amount
Écrivez le type après le nom de l'argument afin de savoir quel est le type de la variable d'argument.
Décrivez également le type afin que vous puissiez voir le type de la variable sortie de la fonction.
Cette indication de type est, comme son nom l'indique, une indication, pas une contrainte. Par conséquent, dans la fonction ci-dessus, donnez float au lieu de int au premier montant,
calc_billing_amount(0.5, 100)
Peut également être exécuté. Aucune erreur ne se produira. Vous devez faire attention à ce point.
Lorsque vous utilisez une liste ou un dictionnaire avec un indice de type, ou lorsque plusieurs types tels que float sont OK même avec int, écrivez comme suit.
from typing import Dict, List, Union
def calc_billing_amount(
amount_list: List[int], price_dictionary: Dict[str, Union[int, float]]
) -> int:
billing_amount = 0
for index, (key, value) in enumerate(price_dictionary.items()):
billing_amount += amount_list[index] * value
return int(billing_amount)
Avec en tapant import List, Dict, Union
, importez une liste d'indices de type, un dictionnaire et une Union à utiliser si l'un ou l'autre convient.
Et, par exemple, si l'élément est une liste de type int, utilisez List [int]
.
Si la clé est de type string et que la valeur peut être int ou float, le dictionnaire est
Dict[str, Union[int, float]]
Écrire.
Exécution
amount = [3, 10]
price = {"item1": 100, "item2": 30.5}
calc_billing_amount(amount, price)
Ensuite, «605» est émis.
De plus, si vous souhaitez utiliser la classe d'origine que vous avez définie comme indice de type, écrivez comme suit.
class User:
def __init__(self, name: str, user_type: str):
self.name = name
self.user_type = user_type
def print_user_type(user: "User") -> str:
print(user.user_type)
Je définis ma propre classe User et je définis la fonction print_user_type qui est exécutée avec cet utilisateur comme argument.
Si vous avez la version 3.7 ou ultérieure de Python
from __future__ import annotations
Vous pouvez remplacer «Utilisateur» par Utilisateur en utilisant, mais Google Colaboratory dispose également d'une version Python de 3.6 et nous vous recommandons la méthode d'écriture ci-dessus.
Pour exécuter la classe avec les indices de type ci-dessus, comme d'habitude,
taro = User("taro", "admin")
print_user_type(taro)
ça ira. Ensuite, la sortie affichera ʻadmin`.
Écrire des indices de type est un problème, mais si vous le divisez en plusieurs classes et méthodes avec une seule responsabilité et que vous l'implémentez, ce sera un problème plus tard.
Surtout quand il s'agit de travailler, les membres de l'équipe voient et utilisent souvent le code qu'ils écrivent. ** Je garderai à l'esprit l'implémentation qui est facile à utiliser pour les autres et facile à utiliser **.
docstring est une description des spécifications et de l'utilisation des classes, des méthodes et des fonctions.
Lorsqu'il s'agit de nombreuses classes et fonctions avec une seule responsabilité, il est difficile de comprendre lors de leur réutilisation.
Comme il est difficile de comprendre avec juste l'indication de type, j'écrirai une docstring comme une explication plus détaillée.
Cependant, comme c'est gênant, je pense qu'une seule ligne docstring convient aux méthodes privées et aux méthodes avec un petit nombre de lignes.
D'autre part, les classes et méthodes utilisées par les autres membres de l'équipe, les classes principales qui occupent une place importante dans les systèmes d'IA, les classes à traitement long, etc.
S'il est plus facile pour les autres membres d'utiliser l'explication détaillée, écrivez la docstring en détail.
Vous pouvez écrire la docstring de n'importe quelle manière, mais généralement ・ ReStructuredText ・ Style Google ・ Style numpy Écrivez dans l'un des.
J'écris dans l'un de ces trois types car je peux ensuite utiliser Sphinx pour le documenter automatiquement. Par conséquent, nous utiliserons la notation docstring qui prend en charge Sphinx.
Cliquez ici pour une explication détaillée des trois types de docstring. Exemple de 3 styles de docstring
J'aime reStructuredText parce que le style Google et le style Numpy ont tendance à être hauts.
La docstring dans reStructuredText est écrite comme suit.
class User:
"""Cette classe indique l'utilisateur du compte qui utilise ce système.
:param name:Nom du compte de l'utilisateur
:param user_type: Type de compte (administrateur ou normal)
:Example:
>>> import User
>>> taro = User("taro", "admin")
"""
def __init__(self, name: str, user_type: str):
self.name = name
self.user_type = user_type
def print_user_type(self):
"""Imprimer le type d'utilisateur avec une instruction d'impression
:pram None:Aucun argument d'entrée
:return: user_Type de sortie sous forme de chaîne
:rtype: str
:Example:
>>> import User
>>> taro = User("taro", "admin")
>>> taro.print_user_type()
admin
"""
print(self.user_type)
Si vous écrivez à Example, vous saurez comment l'utiliser immédiatement, donc il est facile à utiliser pour les autres, et je l'aime bien. La quantité de détails que vous écrivez dépend de la situation et si vous écrivez des exemples, des arguments et des explications pour le retour.
Si vous écrivez comme ci-dessus, dans l'éditeur tel que le code VS, Comme le montre la figure ci-dessous, lorsque vous placez le curseur de la souris sur la partie de programme concernée, cette docstring s'affiche, ce qui facilite la compréhension du programme. (Dans la figure ci-dessous, le curseur de la souris est placé sur print_user_type ())
Dans l'IA, l'apprentissage automatique et l'apprentissage en profondeur, non seulement le modèle entraîné est enregistré, mais également les informations qui peuvent reproduire la formation telles que le pipeline de prétraitement, le réglage des hyper paramètres du modèle et tous les objets nécessaires à l'inférence. Sauvegarder.
La manière dont vous l'enregistrez n'a pas d'importance tant que toutes ces informations sont incluses, mais voici un exemple d'enregistrement avec scicit-learn et PyTorch.
Exemple: pour scicit-learn
from datetime import datetime, timedelta, timezone
import numpy as np
from joblib import dump, load
from sklearn.datasets import load_iris
from sklearn.linear_model import LogisticRegression
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import PolynomialFeatures, StandardScaler
#Préparez l'iris selon les données appropriées
X, y = load_iris(return_X_y=True)
#Prétraitement (caractéristiques standardisées et ajoutées du terme carré)
preprocess_pipeline = Pipeline(steps=[("standard_scaler", StandardScaler())])
preprocess_pipeline.steps.append(("polynominal_features_2", PolynomialFeatures(2)))
#Application du prétraitement
X_preprocessed = preprocess_pipeline.fit_transform(X)
#Préparation du dispositif d'apprentissage
C = 1.2 #Paramètres des hyper paramètres
model = LogisticRegression(random_state=0, C=C)
#Mise en œuvre de l'apprentissage
model.fit(X_preprocessed, y)
#Performance dans les données d'entraînement
accuracy_training = model.score(X_preprocessed, y)
#Prêt à enregistrer diverses données
JST = timezone(timedelta(hours=+9), "JST") #Au Japon, heure
now = datetime.now(JST).strftime("%Y%m%d_%H%M%S") #Obtenir l'heure actuelle
training_info = {
"training_data": "iris",
"model_type": "LogisticRegression",
"hyper_pram_logreg_C": C,
"accuracy_training": accuracy_training,
"save_date": now,
}
save_data = {
"preprocess_pipeline": preprocess_pipeline,
"trained_mode": model,
"training_info": training_info,
}
filename = "./iris_model_" + now + ".joblib"
#sauvegarder
dump(save_data, filename)
Si vous souhaitez charger le contenu enregistré de cette manière,
load_data = load(filename)
#Chargez le contenu chargé
preprocess_pipeline = load_data["preprocess_pipeline"]
model = load_data["trained_mode"]
print(load_data["training_info"])
est.
Dans cet exemple de chargement, training_info est affiché, donc
{'training_data': 'iris', 'model_type': 'LogisticRegression', 'hyper_pram_logreg_C': 1.2, 'accuracy_training': 0.9866666666666667, 'save_date': '20200503_205145'}
Est sortie.
Exemple: pour PyTorch Référence PyTorch SAVING AND LOADING MODELS
PATH = './checkpoint_' + str(epoch) + '.pt'
torch.save({
'epoch': epoch,
'total_epoch': total_epoch,
'model_state_dict': model.state_dict(),
'scheduler.state_dict': scheduler.state_dict()
'optimizer_state_dict': optimizer.state_dict(),
'loss_train': loss_train,
'loss_eval': loss_eval,
}, PATH)
Lors du chargement
#Créez d'abord des objets tels que des modèles
model = TheModelClass() #C'est le même modèle que j'ai enregistré
scheduler = TheSchedulerClass() #C'est la même classe de planificateur que vous avez enregistrée
optimizer = TheOptimizerClass() #Il s'agit de la même classe d'optimisation que vous avez enregistrée
#Charger et donner
checkpoint = torch.load(PATH)
model.load_state_dict(checkpoint['model_state_dict'])
scheduler.load_state_dict(checkpoint['scheduler_state_dict'])
optimizer.load_state_dict(checkpoint['optimizer_state_dict'])
total_epoch = checkpoint['total_epoch']
epoch = checkpoint['epoch']
loss_train = checkpoint['loss_train']
loss_eval = checkpoint['loss_eval']
#Faites ce qui suit en fonction de l'apprentissage ou du raisonnement
model.train()
# model.eval()
Si vous enregistrez le jeu de données d'apprentissage en profondeur et le chargeur de données sur le point de contrôle, le fichier de sauvegarde sera trop volumineux, donc enregistrez-les séparément.
#Dataset, stockage du chargeur de données
torch.save(trainset, './trainset.pt')
torch.save(trainloader, './dataloader.pt')
Lors du chargement
trainset = torch.load('./trainset.pt')
trainloader = torch.load('./dataloader.pt')
C'est tout pour sauvegarder et charger avec PyTorch.
Qu'il s'agisse de scikit-learn ou de PyTorch, peu importe comment vous l'enregistrez.
Cependant, je n'ai enregistré que le modèle et plus tard
** "Comment ce modèle a-t-il été appris!?" ** ** "Quel type de performance est ce modèle!?" ** ** "Il n'y a pas de tuyau de prétraitement formé pour remplir ce modèle !!" **
Faites attention de ne pas vous mettre dans une telle situation.
** * Remarque 1 **: La raison pour laquelle le prétraitement et le modèle ne sont pas combinés en un seul pipeline dans l'exemple de scicit-learn est que le prétraitement peut être exécuté par une autre ressource et jeté dans l'API du modèle. En particulier, lorsque vous souhaitez traiter une grande quantité de données à haute vitesse, vous voulez le rendre facile à utiliser lorsque seul le prétraitement est distribué à d'autres endroits. De plus, le prétraitement est le même et il existe un désir de ne détourner que ce pipeline de prétraitement lors de la formation de différents modèles.
** * Remarque 2 **: J'expliquerai les points à noter lors de la création de votre propre classe de prétraitement plus tard. Ici, lors du chargement d'un pipeline de prétraitement qui utilise une classe auto-fabriquée, etc., importez non seulement la classe de pré-traitement sckit-learn normale avant le chargement, mais importez également la classe auto-créée, etc. Puis chargez-le. Notez que si la classe développée n'est pas importée lors du chargement, une erreur se produira lors du chargement.
Plus les classes, méthodes et fonctions sont subdivisées, plus leur dénomination devient importante.
Si vous regardez le nom, "Que faites-vous? C'est-à-dire quelle responsabilité avez-vous, qu'est-ce que l'entrée et qu'est-ce que la sortie?" Idéalement, vous devriez le savoir.
Cependant, c'est difficile pour les Japonais.
Aucun commentaire n'est nécessaire dans le monde de la programmation, si vous regardez la dénomination du code, c'est un commentaire, Il y a aussi une manière générale de penser, La capacité des Japonais à comprendre la différence entre le vocabulaire anglais et les nuances est difficile.
De plus, dans le cas de la science des données et de l'IA, l'algorithme lui-même est compliqué, donc Il est difficile pour les novices de voir l'implémentation non commentée et de comprendre le contenu d'une classe ou d'une méthode.
Cependant, la méthode de dénomination doit être aussi simple que possible à transmettre.
Le minimum que je veux protéger est
[1] Utiliser la nomenclature pour les noms de classes et les noms de variables
[2] Les noms de méthode et de fonction commencent par un verbe
[3] Dans le cas de la valeur de vérité (type booléen) d'une variable membre, elle peut commencer par les verbes suivants. (Exemple) is_admin, has_item, can_drive, etc.
[4] Utilisons codic etc. https://codic.jp/engine
Le try-catch d'erreur (exception) est une partie fastidieuse qui n'est pas du tout amusante, et c'est une douleur (je pense) pour les scientifiques des données et les ingénieurs en IA.
Il n'y a pas de problème si l'échelle du projet se termine avec uniquement Jupyter Notebook, mais la gestion des erreurs est importante lors de la mise en œuvre de l'IA dans le système. Lorsqu'une exception se produit, l'ensemble du processus système s'arrête.
Par conséquent, dans le code d'implémentation Assurez-vous que l'essai est au niveau supérieur (autrement dit, évitez les situations où le code n'est pas en cours d'essai :).
Assurez-vous de lire le commentaire officiel de Python. Official 8. Erreurs et exceptions
Exemple: lors de la définition d'une fonction à diviser
def func_division(a, b):
ret = a/b
return ret
Si tu fais ça,
ans = func_division(10, 0)
Quand quelque chose comme ça arrive, une erreur se produit et tout le programme s'arrête.
Alors
def func_division(a, b):
try:
ret = a/b
return ret
except:
print("Une exception s'est produite")
Écrire.
Ce n'est pas sufisant. Si une exception qui se produit à partir du contenu de traitement peut être prédite, gérez-la correctement avec cette erreur et gérez l'exception en conséquence.
def func_division(a, b):
try:
ret = a/b
return ret
except ZeroDivisionError as err:
print('Une exception de division 0 s'est produite:', err)
except:
print("Une exception inattendue s'est produite")
Si c'est le cas
ans = func_division(10, 0)
Quand vient
"Une exception de division 0 s'est produite: division par zéro"
Est la sortie,
ans = func_division("hoge", "fuga")
Et si une entrée non numérique est donnée
"Une exception inattendue s'est produite" Est sortie.
Étant donné que la mise en œuvre de l'IA dans les systèmes réels s'accélérera à l'avenir, ce sera un problème pour les personnes qui ne peuvent écrire des programmes qu'au niveau de Jupyter Notebook (je pense).
Il faut être conscient que "try est au niveau supérieur (c'est-à-dire éviter les situations où le code n'est pas en try :)".
** * Remarques **: Réduisez la plage d'essai Try-catch seul peut être un problème, mais cela ne signifie pas que vous devez mettre beaucoup de traitement (plusieurs lignes) en un seul essai. Je ne sais pas où l'exception s'est produite. Try-catch dans une unité de traitement significative. Aussi, comme je l'expliquerai plus tard, lorsqu'il y a plusieurs lignes dans une méthode, il est souvent nécessaire de la scinder en une autre méthode.
Pour les scientifiques des données et les ingénieurs en IA, il est courant de produire une instruction d'impression et de vérifier l'état, mais lors de son intégration dans le système, l'instruction d'impression est gênante, alors écrivez-la correctement sous forme de journal.
L'utilisation du journal est la suivante.
import logging
logger = logging.getLogger(__name__)
#Créer une valeur à mettre dans le journal de manière appropriée
total_epoch = 1000
epoch = 100
loss_train = 5.44444
#Contenu à enregistrer par journal
log_list = [total_epoch, epoch, loss_train]
#Enregistrer dans le journal
logger.info(
"total_epoch: {0[0]}, epoch: {0[1]}, loss_train: {0[2]:.2f}".format(log_list)
)
#Sortez le contenu enregistré dans le journal et vérifiez (pour confirmation maintenant. Initialement inutile)
print("total_epoch: {0[0]}, epoch: {0[1]}, loss_train: {0[2]:.2f}".format(log_list))
Dans ce cas, si vous vérifiez le contenu journalisé avec l'instruction d'impression,
total_epoch: 1000, epoch: 100, loss_train: 5.44
Il est devenu.
Ici, «{0 [2]: .2f}» signifie que la deuxième liste reçue au format .format est affichée jusqu'à 2 chiffres.
Il existe de nombreuses façons d'écrire en Python, que ce soit un journal ou une instruction d'impression, S'il y a beaucoup de variables à écrire, je les écrirai sous forme de liste comme ci-dessus.
Non seulement logger.info, mais aussi logger.debug, logger.warning, logger.error, etc., le niveau de journalisation change en fonction de la situation.
Au minimum, l'atmosphère dans l'exemple ci-dessus est bonne, mais le monde des journaux est profond.
C'est également une bonne idée de lire la documentation officielle du journal.
@paulxll, plus de conseils ajoutés: ensoleillé:
J'ai reçu un exemple utilisant f-string (en supposant Python 3.8). Veuillez également vous référer à ceci ♪
#Créer une valeur à mettre dans le journal de manière appropriée
total_epoch = 1000
epoch = 100
loss_train = 5.44444
#Enregistrer dans le journal
logger.info(f"{total_epoch=}, {epoch=}, loss_train: {loss_train=:.2f}")
Remarques: Ce qui précède explique comment écrire en Python 3.8 ou supérieur. Pour la version 3.7 ou inférieure
logger.info(f"total_epoch: {total_epoch}, epoch: {epoch}, loss_train: {loss_train:.2f}")
(Merci pour ton conseil: ensoleillé :).
Le nombre maximum d'arguments de fonction et de méthode est de trois. Évitez plus de 4 (dans mon cas).
S'il y a beaucoup d'arguments, il sera difficile de comprendre comment utiliser la fonction, et il sera difficile de préparer et de gérer les tests unitaires.
Si vous voulez prendre beaucoup d'arguments, utilisez une variable de type dictionnaire telle que hogehoge_config, etc., et transmettez-la à la fonction comme une variable de dictionnaire.
Exemple: fonction de calcul complexe (Je suis désolé d'avoir expliqué ci-dessus pour écrire la gestion des exceptions, mais la gestion des exceptions est omise car elle est gênante)
def func_many_calculation(a, b, c, d, e):
ret = a*b*c/d/e
return ret
Défini comme ans = func_many_calculation(10, 2, 3, 5, 2)
Si vous utilisez, il y a trop d'arguments et c'est gênant.
Cela peut être une source d'erreurs.
Ainsi, par exemple,
def func_many_calculation(func_config):
a = func_config["a"]
b = func_config["b"]
c = func_config["c"]
d = func_config["d"]
e = func_config["e"]
ret = a*b*c/d/e
return ret
Défini comme func_config = {"a": 10, "b": 2, "c": 3, "d": 5, "e": 2}
Créer une variable à affecter dans un dictionnaire
ans = func_many_calculation(func_config)
Et courir.
Cependant, cette façon d'écrire est trop compliquée pour définir la fonction, donc
def func_many_calculation(a, b, c, d, e):
ret = a*b*c/d/e
return ret
Et, dans la partie définition de la fonction, écrivez beaucoup d'arguments,
Dans la partie à exécuter, essayez de réduire le nombre d'arguments
func_config = {"a": 10, "b": 2, "c": 3, "d": 5, "e": 2}
ans = func_many_calculation(**func_config)
C'est bon à dire.
** * Remarques : Il s'agit d'une méthode d'écriture d'argument inconnue appelée ** func_config
.
Ce «» signifie une opération de décompression pour les variables de dictionnaire.
Alors ici,
func_many_calculation(**func_config)
Est
func_many_calculation (func_config [" a "], func_config [" b "], ..., func_config [" e "])
Veux dire.
Décompresser les variables du dictionnaire avec **
est pratique, alors rendons-le disponible.
* args
et ** kwargs
Je ne sais pas ce que sont «* args» et «** kwargs», mais je les utilise positivement! !! Il n'y a pas de tel data scientist ou ingénieur IA
"Je ne sais pas ce que c'est, mais quand je regarde le référentiel d'implémentation de l'article, je vois beaucoup de * args
et de ** kwargs
. "
Je pense qu'il existe de nombreuses expériences de ce type. Faites-vous des amis avec * args
et ** kwargs
.
Après l'explication de l'opération de déballage des variables de type dictionnaire en 3.4, «* args» et «** kwargs» ne font pas peur.
ʻArgsest une abréviation pour arguments, ce qui signifie argument en japonais.
kwargs` est une abréviation pour les arguments de mot-clé.
Où «*» est l'opération de décompression de la variable de liste.
**
est une opération de décompression des variables de dictionnaire, comme expliqué en 3.4.
Lorsque ces «*» et «**» sont utilisés dans l'argument de la fonction, cela devient comme suit.
Par exemple
def func_args_kwargs(*args, **kwargs):
print(args)
if len(args) >= 2:
print(args[1])
print(kwargs)
flg_a = kwargs.pop("flg_a", False)
print(flg_a)
Si vous définissez une fonction comme
func_args_kwargs(10, 20)
Mettra les arguments d'entrée 10 et 20 dans args
(10, 20) 20 {} False
Est sortie.
Ensuite, utilisez des variables de type dictionnaire pour l'entrée,
func_args_kwargs(10, **{"flg_a":True})
Lorsque vous exécutez, les arguments autres que le premier dictionnaire vont dans args, le dictionnaire entre dans kwargs,
(10,) {'flg_a': True} True
Est sortie.
* args
et ** kwargs
sont appelés ** arguments de longueur variable **.
Je vais expliquer pourquoi nous utilisons de tels arguments de longueur variable * args
et ** kwargs
.
Il y a trois raisons d'utiliser des arguments de longueur variable.
La première raison est qu'elle peut être exécutée même si l'argument de la fonction contient des extras.
Par exemple
def func_args_kwargs2(a, *args, **kwargs):
print(a)
Quand tu cours
func_args_kwargs2(10, 20, 30)
La sortie de
10
Et peut être exécuté sans erreur.
La deuxième raison est que si vous souhaitez étendre une fonction et augmenter le nombre d'arguments ultérieurement, vous pouvez la recevoir avec * args
afin de ne pas avoir à réécrire le contenu de la fonction ou la partie de la définition d'argument.
La troisième raison est d'utiliser «* args» et «** kwargs» comme la partie qui reçoit les arguments qui sont facultatifs et peuvent ou non être passés à l'exécution comme arguments lors de l'exécution d'une fonction ou d'une méthode. Faire.
Dans ce cas, définissez la valeur par défaut lorsque «* args» ou «** kwargs» n'est pas reçu.
Exemple:
def func_args_kwargs3(a, *args, **kwargs):
b = kwargs.pop("b", 2.0)
print(a*b)
Défini comme
func_args_kwargs3(3.0)
La sortie de est 6.0. La valeur par défaut 2.0 est utilisée pour b dans la fonction.
func_args_kwargs3(3, **{"b":4.0})
Dans le cas de, la sortie sera 12,0. B dans la fonction est 4.0 donné comme argument.
Ce qui précède est la fonction de «* args» et «** kwargs».
** * Remarques : Dans mon cas, j'utilise rarement * args
et ** kwargs
au moment de l'implémentation.
Pour les implémentations OSS et papier publiées, « args» et «* kwargs» sont couramment utilisés.
Dans le cas de ces développements, le degré d'incertitude est élevé car de nombreuses personnes utilisent le code d'implémentation.
Donc, comme indiqué dans la première raison, utilisez-vous * args
, ** kwargs
pour que cela fonctionne même s'il contient des arguments inutiles? Je pense.
En Python, les instructions if sont souvent écrites avec un opérateur ternaire et combinées en une seule ligne. Habitons-nous-y. (Je ne l'utilise pas beaucoup pour écrire des livres, mais je l'utilise car le code d'implémentation a moins de lignes et est plus facile à lire.)
#Déterminez si c'est pair ou impair
num = 10
if num % 2 == 0:
print("Même")
else:
print("Impair")
Est
#Déterminez si c'est pair ou impair
num = 10
print("Même") if num % 2 == 0 else print("Impair")
Écrire.
L'exemple dans l'instruction d'impression n'est pas bon. Au moment de la substitution, ce sera comme suit.
num = 10
a = "Même" if num % 2 == 0 else "Impair"
# a = "Même" if num % 2 == 0 else a = "Impair" #Cela entraînera une erreur
print(a)
@Nabetani, plus de conseils ajoutés: ensoleillé:
print("Même") if num % 2 == 0 else print("Impair")
#Que
print("Même" if num % 2 == 0 else "Impair")
#Je pense que c'est préférable.
Ma situation
--Utilisez foo if cond else bar si vous avez besoin de la valeur du résultat de l'opération conditionnelle.
C'est dit.
sklearn conforme est une classe qui hérite de BaseEstimator, TransformerMixin, ClassifierMixin de scikit-learn, etc. et l'implémente afin qu'elle puisse être gérée par la classe Pipeline de scikit-learn avec d'autres objets scikit-learn.
Si vous rendez votre propre classe de prétraitement ou votre modèle compatible sklearn, c'est pratique car il peut être utilisé en l'incorporant dans le pipeline de scicit-learn.
Par exemple, s'il s'agit d'une classe de prétraitement, écrivez comme suit. Hériter de Transformer Mixin et Base Estimator.
from sklearn.base import BaseEstimator, ClassifierMixin, TransformerMixin
from sklearn.utils.validation import check_array, check_is_fitted, check_X_y
class TemplateTransformer(TransformerMixin, BaseEstimator):
"""Exemple de classe de pré-traitement"""
def __init__(self, demo_param='demo'):
self.demo_param = demo_param #Les paramètres à utiliser ultérieurement sont préparés par init
def fit(self, X, y=None):
"""Mise en œuvre de l'apprentissage requis pour le prétraitement. y même si y n'existe pas=Donner sans rien"""
X = check_array(X, accept_sparse=True) # check_array est une fonction de validation pour l'entrée de sklearn
#Le processus d'apprentissage de quelque chose. Voici un exemple n_features_Apprend le paramètre
self.n_features_ = X.shape[1]
#Renvoie le transformateur prétraité lui-même
return self
def transform(self, X):
"""Appliquer le prétraitement à l'argument X"""
#Paramètres à apprendre lors de l'application du prétraitement (ici n_features_) Vérifiez s'il y a
check_is_fitted(self, 'n_features_')
#Validation de l'entrée sklearn
X = check_array(X, accept_sparse=True)
#Certains processus de conversion
X_transformed = hogehoge(X)
return X_transformed
Dans le cas d'une classe de modèle, ClassifierMixin et BaseEstimator sont hérités. Ce qui suit est une image d'apprentissage avec un enseignant, mais je l'écrirai de la même manière pour apprendre sans enseignant.
from sklearn.base import BaseEstimator, ClassifierMixin, TransformerMixin
from sklearn.utils.validation import check_array, check_is_fitted, check_X_y
class TemplateClassifier(ClassifierMixin, BaseEstimator):
"""Exemple de classe de modèle"""
def __init__(self, demo_param="demo"):
self.demo_param = demo_param #Les paramètres à utiliser ultérieurement sont préparés par init
def fit(self, X, y):
"""Mise en œuvre de l'apprentissage. Même si y n'existe pas en raison d'un apprentissage supervisé, etc.=Donner sans rien"""
#Validation de l'entrée sklearn
X, y = check_X_y(X, y)
#Un peu d'apprentissage
self.fugafuga = piyopiyo(X, y)
#Renvoie l'apprenant appris (modèle) lui-même
return self
def predict(self, X):
"""Inférence de données inconnues"""
#Vérifiez s'il y a des paramètres (ici fugafuga) qui doivent être appris avant l'inférence
check_is_fitted(self, ["fugafuga"])
#Validation de l'entrée sklearn
X = check_array(X)
#Déduire
y_predicted = self.fugafuga(X)
return y_predicted
Lors de la mise en œuvre de la conformité sklearn, il est recommandé de modifier le modèle en fonction du modèle publié par scikit-learn.
Developing scikit-learn estimators
Cliquez ici pour consulter le modèle d'implémentation compatible sklearn
Un décorateur est quelque chose comme «@ hogehoge». Il est souvent attaché au-dessus des noms de méthodes et des noms de fonctions, qu'est-ce que c'est? Je pense que cela passera en réfléchissant.
Mais faisons des amis avec les décorateurs.
Il existe des décorateurs standard en Python et des décorateurs faits maison.
Un décorateur standard Python commun est
@property
、@staticmethod
、@classmethod
、@abstractmethod
C'est autour.
Vérifiez un par un.
@ property
rend ses variables membres immuables de l'extérieur de la classe.
(Exemple)
class User:
def __init__(self, name: str, user_type: str):
self.name = name
self.__user_type = user_type
@property
def user_type(self):
return self.__user_type
Comme, le type_utilisateur de la classe User est défini par @ property
.
Puis
taro = User("taro", "admin")
print(taro.user_type)
Peut être exécuté sans aucun problème, et ʻadmin` est affiché.
mais,
taro.user_type="normal"
Et, lorsque j'essaye de changer la variable membre définie par @ property
, j'obtiens une erreur.
De cette manière, vous pouvez définir des variables qui ne peuvent pas être modifiées en externe.
(Exemple)
@staticmethod
、@classmethod
class User:
def __init__(self, name: str, user_type: str):
self.name = name
self.user_type = user_type
@staticmethod
def say_hello(name):
print("Hello " + name)
@ staticmethod
et @ classmethod
activent la méthode sans avoir à matérialiser la classe comme un objet.
Si vous le définissez comme ci-dessus et procédez comme suit,
User.say_hello("Hanako")
Le résultat est «Hello Hanako».
Vous pouvez découvrir la différence entre «@ méthode statique» et «@ méthode de classe» lorsque vous y faites face à nouveau.
Un autre décorateur Python standard que vous trouverez est @ abstractmethod
.
Si une méthode d'une classe a cette @ abstractmethod
, la classe enfant qui hérite de cette classe doit implémenter cette méthode.
S'il n'est pas mis en œuvre, une erreur se produira.
Utilisez @ abstractmethod
pour définir une classe abstraite et forcer la classe enfant héritée à définir une méthode.
En général, si vous comprenez ce domaine, est-ce que cela convient à la science des données et à l'IA?
Lorsque vous créez une application Web avec Django, vous verrez à nouveau un décorateur Django, mais vous pouvez le vérifier à ce moment-là.
Ensuite, j'expliquerai mon propre décorateur.
Supposons qu'il existe un processus qui ne peut être exécuté que lorsque user_type est admin dans la classe User.
class User:
def __init__(self, name: str, user_type: str):
self.name = name
self.user_type = user_type
def func_admin_can_do(self):
if self.user_type=="admin":
#Traitement que seul l'administrateur peut effectuer
print("I'm admin.")
else:
print("cannot do this func with auth error.")
Vous pouvez l'écrire comme ceci, mais s'il existe de nombreux autres processus qui ne peuvent être exécutés que lorsque user_type est admin, il est difficile de vérifier avec l'instruction if à chaque fois.
Donc, si vous utilisez un décorateur, cela ressemblera à ceci:
def admin_only(func):
"""Définition de décorateur"""
def wrapper(self, *args, **kwargs):
if self.user_type == "admin":
#Traitement que seul l'administrateur peut effectuer
return func(self, *args, **kwargs)
else:
print("cannot do this func with auth error.")
return wrapper
class User:
def __init__(self, name: str, user_type: str):
self.name = name
self.user_type = user_type
@admin_only
def func_admin_can_do(self):
#Traitement que seul l'administrateur peut effectuer
print("Im admin.")
Avec cette définition, vous pouvez uniquement ajouter le décorateur @ admin_only
et exécuter le processus uniquement lorsque l'utilisateur est administrateur.
Dans les cas où la même chose est écrite plusieurs fois, par exemple lorsqu'il existe de nombreuses méthodes qui déterminent s'il s'agit ou non d'administrateur et la traitent, utilisez un décorateur.
Il est pratique d'utiliser le formateur automatique lors du codage car il sera formaté automatiquement. Pour Python, le noir est populaire de nos jours.
Cependant, si les membres de l'équipe utilisent des formateurs différents, le simple fait de changer le format augmentera les chances que le fichier soit écrasé et que le contenu de git commit soit perturbé.
Par conséquent, lors du développement en équipe, le formateur automatique est unifié.
Par exemple, créez un dossier ".vscode" directement sous le référentiel et placez-y le fichier "settings.json".
dans setting.json
"python.formatting.provider": "black"
J'écrirai à ce sujet pour qu'il soit formaté en noir.
Lorsqu'un membre code, ouvrez ce dossier avec du code VS et exécutez-le. Il reflète les paramètres du .vscode setting.json du référentiel, donc tout le monde a le même style de codage.
Cet article est très utile pour définir le code VS pour Python.
[Mémo de configuration VSCode qui rend la mise en œuvre du modèle d'apprentissage en profondeur explosive](http://shunk031.hatenablog.com/entry/how-to-setup-vscode-for-developing-deep-learning-model?utm_campaign=piqcy&utm_medium= email & utm_source = Revue% 20newsletter)
Je préparerai le contenu mentionné dans cet article et d'autres points dont je voudrais que vous soyez conscient en tant que modèle de demande de tirage sur GitHub.
Créez un dossier ".github" directement sous le référentiel, et créez un fichier "PULL_REQUEST_TEMPLATE.md" dedans.
Ce PULL_REQUEST_TEMPLATE.md est affiché comme modèle pour le contenu publié au moment de la demande d'extraction.
Si vous notez le contenu mentionné dans cet article,
Niveau 1
- [ ]Suivez-vous les conventions de dénomination des variables, des fonctions, des classes et des méthodes?
- [ ]Méthode de dénomination 1: les parties redondantes sont-elles supprimées des noms de variables et de méthodes?
- [ ]La description de l'importation suit-elle les règles?
- [ ]La graine du nombre aléatoire est-elle fixée pour assurer la reproductibilité?
- [ ]Le programme est-il exécuté en tant que fonction?
Niveau 2
- [ ]Méthode de dénomination 2: est-il facile à lire car il est nommé par notation inversée?
- [ ]Les fonctions et méthodes sont-elles courtes avec une seule responsabilité, consciente de S dans SOLID?
- [ ]Les fonctions et méthodes ont-elles des indices de type?
- [ ]La classe, la méthode ou la fonction a-t-elle une docstring?
- [ ]Lors de l'enregistrement du modèle entraîné, enregistrez-vous ensemble des informations telles que le prétraitement et les hyperparamètres?
Niveau 3
- [ ]Méthode de dénomination 3: Le nom est-il donné avec des mots anglais appropriés et des mots partiels pour comprendre la responsabilité?
- [ ]La gestion des exceptions est-elle correctement implémentée?
- [ ]Implémentez-vous correctement la journalisation?
- [ ]Le nombre d'arguments de fonction et de méthode est-il de 3 ou moins?
- [ ] `*args`、`**kwargs`Utilisez-vous correctement?
Niveau 4
- [ ]Êtes-vous en train d'écrire une courte déclaration if avec un opérateur ternaire?
- [ ]Les classes de prétraitement et de modèle sont-elles implémentées dans la conformité sklearn?
- [ ]Utilisez-vous correctement le décorateur?
- [ ]Les paramètres de l'éditeur sont-ils unifiés au sein de l'équipe?
- [ ]Avez-vous préparé et utilisé un modèle pour la demande d'extraction?
est.
Ce qui précède est un résumé des points dont les scientifiques des données et les ingénieurs en IA doivent être conscients lors de la mise en œuvre avec Python, ainsi que le savoir-faire et les astuces de mise en œuvre.
J'ai écrit un article, mais il y a des règles que je n'ai pas complètement suivies. De plus, comme je suis jeune et que j'ai peu d'expérience, j'apprécierais l'avis de mes prédécesseurs selon lequel il serait préférable de le faire.
Et ce n'est pas bon d'être trop strict et à l'étroit, "Chacun dans l'équipe peut se développer confortablement et la qualité est garantie." Il peut être bon d'établir des règles pour chaque équipe sur ces points.
Merci d'avoir lu ce qui précède.
** Liste de sérialisation récente ** [1] [Explication de la mise en œuvre] Comment utiliser la version japonaise de BERT dans Google Colaboratory (PyTorch) [2] [Explication de la mise en œuvre] Classification des actualités Livedoor dans la version japonaise BERT: Google Colaboratory (PyTorch) [3] [Explication de la mise en œuvre] Science du cerveau et apprentissage non supervisé. Classer MNIST par clustering de maximisation de la quantité d'informations [4] [Explication de la mise en œuvre] Classer les actualités de livingoor par apprentissage japonais BERT x non supervisé (clustering de maximisation de la quantité d'informations)
[Clause de non-responsabilité] Le contenu de cet article lui-même est l'opinion / la transmission de l'auteur, pas l'opinion officielle de la société à laquelle l'auteur appartient.
Recommended Posts