Machine Learning : Participation au challenge data ENS
Je suis débutant en ML et en Python et souhaite me faire la main sur ces sujets en participant au challenge data organisé par l'ENS dans le cadre du cours d'intelligence artificielle de Stéphane Mallat au Collège de France.
Parmi ces challenges, celui proposé par l'entreprise Oze paraît intéressant : il s'agit de définir un modèle permettant, à partir des données météo et des données issues des équipements de chauffage et climatisation, de prédire les consommations d'énergie et la température au sein de bâtiments.
Si vous souhaitez en savoir plus :
https://challengedata.ens.fr/partici...challenges/28/
Il n'y a pas grand chose à gagner, mais je le fais pour l'exercice intellectuel !
Et pour ne pas réfléchir en rond, s'il y en a parmi vous qui souhaitent participer à la réflexion, je serais ravi de le faire avec vous dans cette discussion ! ;)
Exploration des données...
Pour faire suite à mon message d'hier, voici ce que l'"exploration" des données m'a permis de voir :
Les données d'entraînement sont constituées d'un ficher de 7500 lignes (correspondant à 7500 bâtiments) et 12116 colonnes, décomposées comme suit :
- index (col 0)
- données physiques de l'immeuble (col 1 à 16)
- données concernant la datation des mesures (col 17 à 19 : jour, mois, année de démarrage des mesures)
- données relatives aux mesures horaires (col 20 à ... : pour chaque type de mesure de température et ventilation, une colonne par heure de mesure, soit sur 1 mois 672 colonnes)
- données météorologiques par heure (col ... à ... : données météo à chaque heure de mesure, comme ci-dessus)
Les données horaires sont données sous la forme nom_data_XX, avec XX nombre d'heures écoulées depuis le début de la mesure (0<XX<671).
Dans le jeu de données, toutes les variables sont "float64" et il n'y a pas de NaN.
Organisation des inputs data
Le benchmark du challenge est ainsi décrit : "Because of the sequential nature of the data, we chose a recurrent neural network as a benchmark. Data: We began by creating our input vectors using, at each time step, both the time series values and the parameters of the building. This amount to an input tensor of shape (672, 37). The input and output values are then normalized to improve convergence of the network. Network: We deployed a simple network in PyTorch, stacking 3 LSTM layers feeding into a fully connected layer. No dropout or batch normalization were applied."
Il va donc falloir créer à partir des données X "paquets", 1 pour chaque type de mesure, avec comme invariants les caractéristiques du bâtiment et un "timestamp".
Le "timestamp" (pour l'instant simplement les colonnes jour mois année) :
Code:
1 2
| time_stamp = X_train.iloc[:,16:19]
time_stamp.head() |
X_train correspond aux données d'entraînement fournies pour le challenge.
L'extraction des premières mesures :
Code:
1 2
| ac_t_conf = X_train.iloc[:, 19:691]
ac_t_conf.head() |
--> à ce stade, je sens bien qu'il y a une boucle à créer pour automatiser le découpage toutes les 672 colonnes, pour créer autant de dataframes que de mesures, mais je ne vois pas bien encore comment (et surtout comment nommer les fichiers de manière compréhensible)
J'ai ensuite "collé" le time stamp avec les premières mesures :
Code:
1 2 3
| ac_t_conf_date = pd.merge(left=time_stamp, right=ac_t_conf, left_on= 'index', right_on= 'index', how='left')
ac_t_conf_date = pd.merge(left=ac_t_conf_date, right=X_train.building_name, left_on='index', right_on='index', how='left')
ac_t_conf_date.head() |
*j'ai ajouté une colonne 'building_name', dont je doute pour l'instant de la pertinence...
J'ai l'impression qu'il faudrait associer chaque donnée à son timestamp (aa/mm/jj/hh) à partir du numéro de colonne pour ensuite appliquer pd.melt et avoir toutes les valeurs en colonne et les timestamp en ligne. Je creuse le sujet sur comment transformer des numéros de colonne en horodatage...
1 pièce(s) jointe(s)
Tentative de reproduction du Benchmark_un petit coup de main serait le bienvenu !
J'ai profité du confinement pour me remettre à la tâche et simplement reproduire le Benchmark disponible sur GitHub.
Après quelques légères modifications* pour pouvoir disposer d'un ficher de réponse, je fais face à de nouvelles difficultés pour pouvoir soumettre le fichier de réponse.
Je vous fais grâce du code plus ou moins copié/collé depuis le Benchmark, pour arriver à la fin (après l'apprentissage) :
On charge le fichier de test :
Code:
1 2
| dataset_eval = OzeEvaluationDataset(dataset_x_path=r"C:\Users\alain\Documents\Data science\DataChallenge\x_test.csv", labels_path=r"C:\Users\alain\Documents\Data science\DataChallenge\ozechallenge_benchmark-master\labels.json")
m_test = len(dataset_eval) |
On établit les prédictions :
Code:
1 2 3 4 5 6 7 8 9
| predictions = np.zeros((m_test, K, 8))
with torch.no_grad():
for idx, line in tqdm(enumerate(dataset_eval), total=m_test):
# Run prediction
netout = net(torch.Tensor(line[np.newaxis, :, :])).numpy()
# De-normalize output
output = netout * (M - m + np.finfo(float).eps) + m
predictions[idx] = output |
On créée le fichier à soumettre pour le challenge :
Code:
1 2 3 4
| lines_output = predictions.reshape((m_test, -1))
csv_header = [f"{var_name}_{k}" for var_name in dataset_eval.labels['X'] for k in range(K)]
pd.DataFrame(lines_output, columns=csv_header).to_csv(r"C:\Users\alain\Documents\Data science\DataChallenge\y_bench120.csv") |
Là, c'est le drame : la plateforme me soumet le message d'erreur suivant 'Invalid Index : the index of the csv file you have submitted does not matcht the index of the test file'.
J'ai donc pris le fichier réponse mis en exemple sur la page du challenge :
Code:
exp = pd.read_csv(r"C:\Users\alain\Documents\Data science\DataChallenge\y_random.csv")
NB : j'ai soumis le fichier y_random sur la plateforme et cela a fonctionné.
J'ai repris mon fichier réponse nouvellement créé :
Code:
rep = pd.read_csv(r"C:\Users\alain\Documents\Data science\DataChallenge\y_bench120.csv")
J'ai regardé les différences dans les indexes :
Code:
1 2 3 4
| ind1 = exp.columns
ind2 = rep.columns
ind1.intersection(ind2)
ind1.difference(ind2) |
Avec la réponse suivante :
Code:
Index(['index'], dtype='object')
J'ai réindexé mon fichier réponse selon l'exemple :
Code:
A = rep.reindex_like(exp)
Puis comparé les indexes :
Code:
1 2
| ind3 = A.columns
ind3.difference(ind1) |
Avec comme réponse :
Code:
Index([], dtype='object')
Que j'interprète comme un ensemble vide (donc pas de différence d'indexes).
Mais en regardant de plus près, on voit des NaN au lieu des données en 7500 dans la colonne index :
Pièce jointe 546673
J'ai donc remplacé les valeurs :
Code:
1 2
| for i in range(500):
A['index'][i]=exp['index'][i] |
Mais cela me donnait des float au lieu d'int, du coup j'ai forcé le truc :
Code:
1 2
| C = B['index'].astype(int)
print(C) |
Puis enfin:
Code:
pd.DataFrame(B, columns=csv_header).to_csv(r"C:\Users\alain\Documents\Data science\DataChallenge\y_bench200.csv")
Mais le nouveau fichier renvoie toujours le même message d'erreur à mon grand désespoir... : 'Invalid Index : the index of the csv file you have submitted does not matcht the index of the test file'
Auriez-vous des idées pour faire matcher les indexes ?
* la principale modification a porté sur le fait de mettre NUM_WORKERS = 0 (au lieu de 4) - étant sous Windows avec Jupyter (je n'ai pas le pourquoi du comment, mais l'essentiel est que cela fonctionne...)
Création des données temporelles - problème non résolu
La dataframe est constituée de données sous la forme mesure_X, avec X le nombre d'heures écoulées depuis le début de l'enregristrement, avec autant de colonnes que de mesures, soit 671 (car 28 jours * 1 mesure/heure).
Je souhaite donc créer sur le même modèle, 671 colonnes, avec une donnée indiquant la date de mesure (à l'heure près), sous la forme jour_X.
J'ai dû reconstituer la date à partir des données de 3 colonnes (une pour le jour, l'année et le mois) :
Code:
1 2 3
| x_train['date_init'] = x_train['init_year'].astype('int').astype('str')+'-'+x_train['init_month'].astype('int').astype('str')+'-'+x_train['init_day'].astype('int').astype('str')
x_train.loc[0:3, 'date_init']
pd.to_datetime(x_train['date_init']) |
J'ai créé les 671 colonnes, à partir de la date initiale :
Code:
1 2 3 4
| from datetime import datetime, timedelta
for i in range(672):
col_name = 'jour_'+str(i)
x_train[col_name] = x_train['date_init'] |
Dans l'idéal, ce serait ce code ci :
Code:
1 2 3 4 5 6 7 8 9
| from datetime import datetime, timedelta
for i in range(672):
col_name = 'jour_'+str(i)
x_train[col_name] = np.nan
for row in x_train.iterrows():
s = x_train.loc[row, 'date_init']
date = datetime.strptime(s, "%Y-%m-%d")
modified_date = date + timedelta(days=i)
x_train.loc[s, col_name] = modified_date |
Mais il renvoie le message d'erreur suivant:
Code:
TypeError: 'Series' objects are mutable, thus they cannot be hashed
Mais je ne parviens pas à faire +1 heure sur chaque et d'une colonne à l'autre... des idées ?