From 09bbd580a53fae2810b910b3356960dafa36cad5 Mon Sep 17 00:00:00 2001 From: Annie Deshaies Date: Thu, 4 Mar 2021 10:51:29 -0500 Subject: [PATCH 1/7] fix permalink posts --- config.toml | 2 +- .../index.fr.md | 31 ++- .../2020-07-29-openlayer-one-year/index.fr.md | 20 +- .../index.en.md | 192 +++++++++--------- .../index.fr.md | 12 +- 5 files changed, 131 insertions(+), 126 deletions(-) diff --git a/config.toml b/config.toml index 0fff6f9d5..f2b02f1d8 100644 --- a/config.toml +++ b/config.toml @@ -14,7 +14,7 @@ canonifyURLs = true enableEmoji = true [permalinks] - post = "/:slug/" + blog = "/:slug/" [params] diff --git a/content/blog/2019-06-16-agregation-classements/index.fr.md b/content/blog/2019-06-16-agregation-classements/index.fr.md index d27f0ec8b..d5ef53f42 100644 --- a/content/blog/2019-06-16-agregation-classements/index.fr.md +++ b/content/blog/2019-06-16-agregation-classements/index.fr.md @@ -2,18 +2,18 @@ title: Agrégation simple de classements author: Samuel Perreault et Stéphane Caron date: '2019-06-16' -slug: Agrégation simple de classements +slug: agregation-simple-de-classements type: post -categories: ["Analytique"] -tags: ["Analytique de sports"] -description: "" -featured: "agregation-cover.png" -featuredpath: "img/headers/" +categories: ['Analytique'] +tags: ['Analytique de sports'] +description: '' +featured: 'agregation-cover.png' +featuredpath: 'img/headers/' --- Il y a quelques jours, des membres de la communauté .Layer se sont réunis dans un chalet autour d'une thématique: l'analyse de données sportives. Lors de ce week-end, nous avons effleuré trois projets reliant sports et analyse de données: -1. Agréger différents *draft rankings* (LNH) pour en former un unique (L-A Vallière-Lavoie, Laurent Caron et Sam Perreault) +1. Agréger différents _draft rankings_ (LNH) pour en former un unique (L-A Vallière-Lavoie, Laurent Caron et Sam Perreault) 2. Un [dashboard](https://stecaron.shinyapps.io/shiny-app/) de données permettant d'identifier les meilleurs jeunes joueurs de tennis de l'ATP (Phil B-l, Sté Caron, Sam Auclair, John Tremblay et J.P. Le Cavalier) 3. Un outil pour trouver le bon régime et le bon programme d'entraînement selon des objectifs (Antoine Buteau) @@ -21,7 +21,7 @@ Cet article sommarise très brièvement le projet d’agrégation de listes de r ## Extraire les données -Pour extraire nos données, nous avons *scrapé* des listes produites par des experts ([mynhldraft.com](http://www.mynhldraft.com/2019-nhl-draft/2019-nhl-draft-rankings/)) en utilisant le *package* R `rvest`: +Pour extraire nos données, nous avons _scrapé_ des listes produites par des experts ([mynhldraft.com](http://www.mynhldraft.com/2019-nhl-draft/2019-nhl-draft-rankings/)) en utilisant le _package_ R `rvest`: ``` library(rvest) @@ -44,7 +44,6 @@ temp <- temp[-1,] head(temp)[,1:3] ``` - ``` ## V1 Future Considerations June 7th ## 1: 1 Jack Hughes @@ -102,11 +101,10 @@ by_player <- by_player[order(by_player$final_selection),]$player[1:10] ``` - ``` -## [1] "Jack Hughes" "Kaapo Kakko" "Bowen Byram" -## [4] "Alex Turcotte" "Trevor Zegras" "Kirby Dach" -## [7] "Dylan Cozens" "Vasili Podkolzin" "Matthew Boldy" +## [1] "Jack Hughes" "Kaapo Kakko" "Bowen Byram" +## [4] "Alex Turcotte" "Trevor Zegras" "Kirby Dach" +## [7] "Dylan Cozens" "Vasili Podkolzin" "Matthew Boldy" ## [10] "Peyton Krebs" ``` @@ -114,7 +112,6 @@ by_player[order(by_player$final_selection),]$player[1:10] Avec le temps qu'il nous restait, nous avons décidé de construire une matrice de distances (entre les joueurs) calculée en considérant le vecteur des rangs: - ``` ## Albert Johansson Albin Grewe Alex Newhook Alex Turcotte Alex Vlasic ## [1,] 33 28 15 3 33 @@ -131,16 +128,16 @@ Nous avons calculé une sorte de variation locale des rangs: ![](canard.png) Naturellement, plus on considère des rangs élevés, plus il y a d'incertitude quant à ceux-ci (du moins cette année). Ce qui se passe après 25 ne fait plus de sens puisque nos données sont censurées à droite. -Nous avons utilisé ceci (en gardant la valeur maximum après le *peak*), pour calculer des distances *somewhat* normalisées (entre les joueurs). +Nous avons utilisé ceci (en gardant la valeur maximum après le _peak_), pour calculer des distances _somewhat_ normalisées (entre les joueurs). -Finalement, la matrice de distances obtenue nous a permis de projeter nos données sur une droite (une seule dimension) : ce qu'on appelle du positionnement multidimensionnel. En fait, nous avons *pluggé* la matrice de distances dans la fonction `cmdscale`. Les valeurs obtenues forment l'axe vertical dans la figure suivante: +Finalement, la matrice de distances obtenue nous a permis de projeter nos données sur une droite (une seule dimension) : ce qu'on appelle du positionnement multidimensionnel. En fait, nous avons _pluggé_ la matrice de distances dans la fonction `cmdscale`. Les valeurs obtenues forment l'axe vertical dans la figure suivante: ![](draft-normalized.png) En gardant un espacement constant sur l'axe horizontal, on peut apprécier à quel point Jack Hughes et Kaapo Kakko se démarque du reste (ils étaient toujours premier et deuxième ...). Comme mentionné en introduction, le repêchage officiel de la LNH avait lieu quelques jours après notre première analyse (figure ci-haut). -Les résultats sont présentés dans la figure ci-dessous: les noms des joueurs sont accompagnés de leur rang observé, ainsi que la différence entre la prédiction de l'agrégation d'experts et le rang observé. +Les résultats sont présentés dans la figure ci-dessous: les noms des joueurs sont accompagnés de leur rang observé, ainsi que la différence entre la prédiction de l'agrégation d'experts et le rang observé. Les noms de joueurs en gris ont été repêchés au rang prévu, les rouges ont été repêchés plus tard que prévu alors que les verts ont été repêché plus tôt que prévu. ![](p-a-d.png) diff --git a/content/blog/2020-07-29-openlayer-one-year/index.fr.md b/content/blog/2020-07-29-openlayer-one-year/index.fr.md index 943deb6ca..9d0fb9cad 100644 --- a/content/blog/2020-07-29-openlayer-one-year/index.fr.md +++ b/content/blog/2020-07-29-openlayer-one-year/index.fr.md @@ -2,13 +2,13 @@ title: OpenLayer, 1 an plus tard author: David Beauchemin date: '2020-08-18' -slug: podcast +slug: openlayer type: post -categories: ["Podcast"] +categories: ['Podcast'] tags: [] description: "Retour sur la première année d'existence d'OpenLayer" -featured: "OpenLayer_YoutubeBanner.jpg" -featuredpath: "img/headers/" +featured: 'OpenLayer_YoutubeBanner.jpg' +featuredpath: 'img/headers/' --- Cela fait maintenant 1 an que j’ai lancé OpenLayer, mon projet de podcast vidéo sur l'IA. 32 épisodes plus tard, beaucoup de choses se sont dites derrière le micro. Ce texte débute par une présentation de mon voyage dans cette aventure et je termine avec un survol de certains moments forts des diverses discussions que j’ai eu la chance d’avoir avec les invités. @@ -19,11 +19,11 @@ D’abord, c’est quoi OpenLayer? Ça se veut un lieu où une _myriade_ de personnes ayant des profils aux antipodes les uns des autres, mais gravitant autour de cette _bébitte_ qu’est l'IA, viennent partager de leur expérience. De l’expert en recherche en apprentissage automatique en passant par l’avocat en propriété intellectuelle jusqu’aux ressources humaines ou la santé. Nous avons eu droit à beaucoup d’informations sur cet univers selon différents acteurs du milieu. -Cette idée d'avoir un podcast vidéo m’est venue il y a environ 2-3 ans en écoutant Mike Ward sous écoute (j’ai écouté l’ensemble des épisodes ou à peu près #BRAG). Je me suis dit, il y a quelque chose d’intéressant à faire avec cela. Par la suite, les choses se sont mises en branle lorsque j’ai quitté mon appartement d’étudiant laid sur l'avenue Myrand. Un mur « blanc » vieilli par le soleil et les années en arrière-plan accompagné du son de mon voisin qui *game* en gueulant (littéralement), ce n’est pas l’arrière-plan le plus excitant mettons. +Cette idée d'avoir un podcast vidéo m’est venue il y a environ 2-3 ans en écoutant Mike Ward sous écoute (j’ai écouté l’ensemble des épisodes ou à peu près #BRAG). Je me suis dit, il y a quelque chose d’intéressant à faire avec cela. Par la suite, les choses se sont mises en branle lorsque j’ai quitté mon appartement d’étudiant laid sur l'avenue Myrand. Un mur « blanc » vieilli par le soleil et les années en arrière-plan accompagné du son de mon voisin qui _game_ en gueulant (littéralement), ce n’est pas l’arrière-plan le plus excitant mettons. ### Les débuts -*Fast-forward* à l’été 2019, pour commencer, j’ai débuté en invitant un collègue et ami, [Nicolas Garneau](https://youtu.be/x9Zo-F0PWQE), pour rendre l'expérience plus « facile » puisque je le connais bien. C’est un épisode intéressant puisqu’on aborde différents aspects personnels de sa vie, mais aussi de sujets plus techniques comme la qualité du code en recherche et ce qui se veut être une introduction à la reproductibilité en science. +_Fast-forward_ à l’été 2019, pour commencer, j’ai débuté en invitant un collègue et ami, [Nicolas Garneau](https://youtu.be/x9Zo-F0PWQE), pour rendre l'expérience plus « facile » puisque je le connais bien. C’est un épisode intéressant puisqu’on aborde différents aspects personnels de sa vie, mais aussi de sujets plus techniques comme la qualité du code en recherche et ce qui se veut être une introduction à la reproductibilité en science. ### L’évolution @@ -39,16 +39,16 @@ Je vais t’avouer que je n’y pense pas vraiment, si ce jour arrive, je vais v À travers les divers épisodes, plusieurs sujets ont été abordés avec les invités. Notamment l’aspect technique de l’IA ou de l’apprentissage automatique, le droit et la législation des algorithmes et leurs propriétés intellectuelles, la mise en production des solutions et j’en passe. Je pourrais facilement faire un texte avec chaque épisode, c’est pourquoi je vais plutôt recenser ici quelques moments forts qui m’ont personnellement marqué et qui m'ont amené à réfléchir sur certains aspects de mon travail. -Premièrement, en réfléchissant au contenu des épisodes, j’en suis venu à un constat; le plus important n’est pas la technologie, ton expertise ou *whatever*, mais principalement la vision que tu portes sur le problème et comment tu l’utilises pour adresser le problème. +Premièrement, en réfléchissant au contenu des épisodes, j’en suis venu à un constat; le plus important n’est pas la technologie, ton expertise ou _whatever_, mais principalement la vision que tu portes sur le problème et comment tu l’utilises pour adresser le problème. Prenons par exemple, [Alexandre Dubé-Côté](https://youtu.be/YAnDc-GicpY), qui a vraiment utilisé une vision orientée affaires pour nous expliquer comment bien concevoir des solutions d'IA. Cette dernière doit répondre à un besoin, qu'il soit d’affaires, académique, ou social, sa solution doit répondre à un besoin. -[Helene-Sarah Becotte](https://youtu.be/yRiEF7_i13Q) utilise beaucoup ce discours lorsqu'elle parle de son utilisation des mathématiques. La vision de beaucoup de gens (dans mon entourage non universitaire, mettons) des maths c’est que c’est *useless*. Alors que si on lui donne une vision plus utilitaire, donc répondre à un besoin, bien les maths prennent tout leur sens. Le meilleur exemple de cela est son excellent [blog](https://helenebecotte.com/2019/11/24/optimise-ta-productivite-en-remplacant-ton-cafe-par-du-the/) sur l’optimisation de ta consommation de café pour être le plus productif possible. +[Helene-Sarah Becotte](https://youtu.be/yRiEF7_i13Q) utilise beaucoup ce discours lorsqu'elle parle de son utilisation des mathématiques. La vision de beaucoup de gens (dans mon entourage non universitaire, mettons) des maths c’est que c’est _useless_. Alors que si on lui donne une vision plus utilitaire, donc répondre à un besoin, bien les maths prennent tout leur sens. Le meilleur exemple de cela est son excellent [blog](https://helenebecotte.com/2019/11/24/optimise-ta-productivite-en-remplacant-ton-cafe-par-du-the/) sur l’optimisation de ta consommation de café pour être le plus productif possible. Deuxièmement, sois bon dans au moins deux domaines, je crois que c’est [Gabrielle Trudeau](https://youtu.be/xN7Z8jjxRDU) qui cite son paternel lui ayant dit cette phrase : dans la vie tu dois avoir deux expertises (je n’ai pas réussi à retrouver le moment exact). J’ai trouvé cela fascinant comme concept et ça m’a fait penser à cette discussion avec [Charles Demontigny](https://youtu.be/0TcH7XWLkIY) qui dit sensiblement la même chose : qu’il aime apprendre 80% de l’essentiel sur plusieurs domaines plutôt que de connaître un seul domaine à 100% ([principe de Pareto](https://fr.wikipedia.org/wiki/Principe_de_Pareto)). J’ai trouvé intéressante l'idée de connaître relativement bien plusieurs domaines, particulièrement de nos jours où la multidisciplinarité est de plus en plus présente dans les projets au sein des entreprises. De connaître au minimum le vocabulaire et les différentes notions de base d’un secteur permet de faciliter la communication. Particulièrement lorsque l’on sait que l'IA s'applique à plusieurs domaines (génie, santé, finance ...). -Ce qui m’amène à mon troisième point, *do the extra miles*! À travers les diverses discussions, j’ai eu à de multiples occasions l'impression que c’est vraiment ce qui peut faire la différence dans un parcours. Par exemple, se construire un porte-folio de solution, développer son image professionnelle sur les réseaux sociaux (LinkedIn, Twitter, Tik Tok?!?!). Le point est, fait en un petit peu plus que les autres et ça va paraître sur le _long run_. +Ce qui m’amène à mon troisième point, _do the extra miles_! À travers les diverses discussions, j’ai eu à de multiples occasions l'impression que c’est vraiment ce qui peut faire la différence dans un parcours. Par exemple, se construire un porte-folio de solution, développer son image professionnelle sur les réseaux sociaux (LinkedIn, Twitter, Tik Tok?!?!). Le point est, fait en un petit peu plus que les autres et ça va paraître sur le _long run_. -Écoute, je pourrais encore continuer, mais l’idée reste de faire un retour sur cette expérience plus qu’analyser chaque moment. J’espère continuer cette aventure encore longtemps et merci à tous mes ***number one fan***. +Écoute, je pourrais encore continuer, mais l’idée reste de faire un retour sur cette expérience plus qu’analyser chaque moment. J’espère continuer cette aventure encore longtemps et merci à tous mes **_number one fan_**. diff --git a/content/blog/2020-08-19-train-a-sequence-model-with-poutyne/index.en.md b/content/blog/2020-08-19-train-a-sequence-model-with-poutyne/index.en.md index e2fc4760e..0f71f2f6c 100644 --- a/content/blog/2020-08-19-train-a-sequence-model-with-poutyne/index.en.md +++ b/content/blog/2020-08-19-train-a-sequence-model-with-poutyne/index.en.md @@ -2,31 +2,31 @@ title: Training a Recurrent Neural Network (RNN) using PyTorch author: David Beauchemin and Marouane Yassine date: '2020-11-18' -slug: machine learning +slug: training-rnn-using-pytorch type: post -categories: ["Python", "Machine learning", "RNN", "PyTorch", "Poutyne", "LSTM", "Address parsing"] +categories: ['Python', 'Machine learning', 'RNN', 'PyTorch', 'Poutyne', 'LSTM', 'Address parsing'] tags: [] -description: "Train an RNN for address parsing" -featured: "we-mastered-the-art-of-programming-cover.jpeg" -featuredpath: "img/headers/" +description: 'Train an RNN for address parsing' +featured: 'we-mastered-the-art-of-programming-cover.jpeg' +featuredpath: 'img/headers/' --- -> In this article, we will train an RNN, or more precisely, an LSTM, to predict the sequence of tags associated with a -given address, known as address parsing. +> In this article, we will train an RNN, or more precisely, an LSTM, to predict the sequence of tags associated with a +> given address, known as address parsing. > Also, the article is available in a [Jupyter Notebook](https://github.com/dot-layer/blog/blob/master/content/blog/2020-08-19-train-a-sequence-model-with-poutyne/article_notebook.ipynb) or in a [Google Colab Jupyter notebook](https://colab.research.google.com/github/dot-layer/blog/blob/master/content/blog/2020-08-19-train-a-sequence-model-with-poutyne/article_notebook_colab.ipynb). > > Before starting this article, we would like to disclaim that this tutorial is greatly inspired by an online tutorial David created for the Poutyne framework. Also, the content is based on a recent [article](https://arxiv.org/abs/2006.16152) we wrote about address tagging. However, there are differences between the present work and the two others, as this one is specifically designed for the less technical reader. - -Sequential data, such as addresses, are pieces of information that are deliberately given in a specific order. In other words, they are sequences with a particular structure; and knowing this structure is crucial for predicting the missing entries of a given truncated sequence. For example, +Sequential data, such as addresses, are pieces of information that are deliberately given in a specific order. In other words, they are sequences with a particular structure; and knowing this structure is crucial for predicting the missing entries of a given truncated sequence. For example, when writing an address, we know, in Canada, that after the civic number (e.g. 420), we have the street name (e.g. du Lac). Hence, if one is asked to complete an address containing only a number, he can reasonably assume that the next information that should be added to the sequence is a street name. Various modelling approaches have been proposed to make predictions over sequential data. Still, more recently, deep learning models known as Recurrent Neural Network (RNN) have been introduced for this type of data. The main purpose of this article is to introduce the various tricks (e.g., padding and packing) that are required for training an RNN. Before we do that, let us define our "address" problem more formally and elaborate on what RNNs (and LSTMs) actually are. ## Address Tagging -Address tagging is the task of detecting and tagging the different parts of an address such as the civic number, + +Address tagging is the task of detecting and tagging the different parts of an address such as the civic number, the street name or the postal code (or zip code). The following figure shows an example of such a tagging. ![address parsing canada](address_parsing.png) @@ -40,12 +40,12 @@ A dedicated type of neural networks was specifically designed for this kind of t In brief, an RNN is a neural network in which connections between nodes form a temporal sequence. It means that this type of network allows previous outputs to be used as inputs for the next prediction. -For more information regarding RNNs, have a look at Stanford's freely available [cheastsheet](https://stanford.edu/~shervine/teaching/cs-230/cheatsheet-recurrent-neural-networks). +For more information regarding RNNs, have a look at Stanford's freely available [cheastsheet](https://stanford.edu/~shervine/teaching/cs-230/cheatsheet-recurrent-neural-networks). For our purpose, we do not use the vanilla RNN, but a widely-use variant of it known as long short-term memory (LSTM) network. This latter, which involves components called gates, is often preferred over its competitors due to its better stability with respect to gradient update (vanishing and exploding gradient). -To learn more about LSTMs, see [here](http://colah.github.io/posts/2015-08-Understanding-LSTMs/) for an in-depth explanation. +To learn more about LSTMs, see [here](http://colah.github.io/posts/2015-08-Understanding-LSTMs/) for an in-depth explanation. -For now, let's simply use a single layer unidirectional LSTM. We will, later on, explore the use of more layers and a bidirectional approach. +For now, let's simply use a single layer unidirectional LSTM. We will, later on, explore the use of more layers and a bidirectional approach. ### Word Embeddings @@ -55,9 +55,9 @@ between the word `king` and `queen` is gender. So logically, if we remove the ve `female`, we should obtain the vector corresponding to `queen` (i.e. `king - male + female = queen`). That being said, this kind of representation is usually made in high dimensions such as `300`, which makes it impossible for humans to reason about them. Neural networks, on the other hand, can efficiently make use of the implicit relations despite their high dimensionality. -We therefore fix our LSTM's input and hidden state dimensions to the same sizes as the vectors of embedded words. +We therefore fix our LSTM's input and hidden state dimensions to the same sizes as the vectors of embedded words. For the present purpose, we will use the -[French pre-trained](https://fasttext.cc/docs/en/crawl-vectors.html) fastText embeddings of dimension `300`. +[French pre-trained](https://fasttext.cc/docs/en/crawl-vectors.html) fastText embeddings of dimension `300`. ### The PyTorch Model @@ -86,7 +86,7 @@ from poutyne import set_seeds from poutyne.framework import Experiment ``` -Now, let's create a single (i.e. one layer) unidirectional LSTM with `input_size` and `hidden_size` of `300`. We +Now, let's create a single (i.e. one layer) unidirectional LSTM with `input_size` and `hidden_size` of `300`. We will explore later on the effect of stacking more layers and using a bidirectional approach. > See [here](https://discuss.pytorch.org/t/could-someone-explain-batch-first-true-in-lstm/15402) why we use the `batch_first` argument. @@ -103,14 +103,13 @@ lstm_network = nn.LSTM(input_size=dimension, batch_first=True) ``` - ## Fully-connected Layer + Since the output of the LSTM network is of dimension `300`, we will use a fully-connected layer to map it into -a space of equal dimension to that of the tag space (i.e. number of tags to predict), that is 8. +a space of equal dimension to that of the tag space (i.e. number of tags to predict), that is 8. Finally, since we want to predict the most probable tokens, we will apply the softmax function on this layer (see [here](https://en.wikipedia.org/wiki/Softmax_function) if softmax does not ring a bell). - ```python input_dim = dimension #the output of the LSTM tag_dimension = 8 @@ -120,8 +119,8 @@ fully_connected_network = nn.Linear(input_dim, tag_dimension) ## Training Constants -Now, let's set our training constants. We first specify a CUDA (GPU) device for training (using a CPU takes way too long, -if you don't have one, you can use the Google Colab notebook). +Now, let's set our training constants. We first specify a CUDA (GPU) device for training (using a CPU takes way too long, +if you don't have one, you can use the Google Colab notebook). Second, we set the batch size (i.e. the number of elements to see before updating the model), the learning rate for the optimizer and the number of epochs. @@ -144,6 +143,7 @@ set_seeds(42) ``` ## The Dataset + The dataset consists of `1,010,987` complete French and English Canadian addresses and their associated tags. Here's an example address @@ -153,7 +153,7 @@ and its corresponding tags `[StreetNumber, StreetName, StreetName, StreetName, Orientation, Municipality, PostalCode, PostalCode]`. -Now let's download our dataset. For simplicity, a `100,000` addresses test set is kept aside, with 80% of the remaining addresses used for training and 20 % used as a validation set. +Now let's download our dataset. For simplicity, a `100,000` addresses test set is kept aside, with 80% of the remaining addresses used for training and 20 % used as a validation set. Also note that the dataset was pickled for simplicity (using a Python `list`). Here is the code to download it. ```python @@ -198,8 +198,10 @@ train_data[:2] # The first two train items ### Vectorize the Dataset -Since we used word embeddings as the encoded representations of the words in the addresses, we need to *convert* the addresses into the corresponding word vectors. In order to do that, we will use a `vectorizer` (i.e. the process of converting words into vectors). This embedding vectorizer will extract, for each word, the embedding value based on the pre-trained French fastText model. We use French embeddings because French is the language in which most of the adresses in our dataset are written. +Since we used word embeddings as the encoded representations of the words in the addresses, we need to _convert_ the addresses into the corresponding word vectors. In order to do that, we will use a `vectorizer` (i.e. the process of converting words into vectors). This embedding vectorizer will extract, for each word, the embedding value based on the pre-trained French fastText model. We use French embeddings because French is the language in which most of the adresses in our dataset are written. + > If you are curious about another solution, the [Google Colab Jupyter notebook](https://colab.research.google.com/github/dot-layer/blog/blob/post%2Fdb_sequence_training_poutyne/content/blog/2020-08-19-train-a-sequence-model-with-poutyne/article_notebook_colab.ipynb) uses Magnitude. + ```python # We use this class so that the download templating of the fastText # script be not buggy as hell in notebooks. @@ -207,7 +209,7 @@ class LookForProgress(TextIOBase): def __init__(self, stdout): self.stdout = stdout self.regex = re.compile(r'([0-9]+(\.[0-9]+)?%)', re.IGNORECASE) - + def write(self, o): res = self.regex.findall(o) if len(res) != 0: @@ -231,14 +233,14 @@ class EmbeddingVectorizer: for word in address.split(): embeddings.append(self.embedding_model[word]) return embeddings - + embedding_vectorizer = EmbeddingVectorizer() ``` -We also need to apply a similar operation to the address tags (e.g. StreetNumber, StreetName). -This time, however, the `vectorizer` needs to convert the tags into categorical values (e.g. StreetNumber -> 0). -For simplicity, we will use a `DatasetBucket` class that will apply the vectorizing process using both -the embedding and the address vectorization process that we've just described during training. +We also need to apply a similar operation to the address tags (e.g. StreetNumber, StreetName). +This time, however, the `vectorizer` needs to convert the tags into categorical values (e.g. StreetNumber -> 0). +For simplicity, we will use a `DatasetBucket` class that will apply the vectorizing process using both +the embedding and the address vectorization process that we've just described during training. ```python class DatasetBucket: @@ -283,7 +285,8 @@ class DatasetBucket: train_dataset_vectorizer = DatasetBucket(train_data, embedding_vectorizer) valid_dataset_vectorizer = DatasetBucket(valid_data, embedding_vectorizer) test_dataset_vectorizer = DatasetBucket(test_data, embedding_vectorizer) -``` +``` + > Here is a example of the vectorizing process. ```python @@ -295,13 +298,13 @@ print(f"The vectorized address is now a list of vectors {address}") print(f"Tag is now a list of integers : {tag}") ``` - ### DataLoader -> We use a first trick, ``padding``. + +> We use a first trick, `padding`. Now, because the addresses are not all of the same size, it is impossible to batch them together; recall that all tensor elements must have the same lengths. But there is a trick: padding! -The idea is simple; we add *empty* tokens at the end of each sequence until they reach the length of the longest one in the batch. For example, if we have three sequences of length ${1, 3, 5}$, padding will add 4 and 2 *empty* tokens respectively to the first two. +The idea is simple; we add _empty_ tokens at the end of each sequence until they reach the length of the longest one in the batch. For example, if we have three sequences of length ${1, 3, 5}$, padding will add 4 and 2 _empty_ tokens respectively to the first two. For the word vectors, we add vectors of 0 as padding. For the tag indices, we pad with -100's. We do so because the [cross-entropy loss](https://pytorch.org/docs/stable/generated/torch.nn.CrossEntropyLoss.html#torch.nn.CrossEntropyLoss) and the accuracy metric both ignore targets with values of -100. @@ -310,29 +313,29 @@ To do the padding, we use the `collate_fn` argument of the [PyTorch `DataLoader` ```python def pad_collate_fn(batch): """ - The collate_fn that can add padding to the sequences so all can have + The collate_fn that can add padding to the sequences so all can have the same length as the longest one. Args: - batch (List[List, List]): The batch data, where the first element - of the tuple is the word idx and the second element are the target + batch (List[List, List]): The batch data, where the first element + of the tuple is the word idx and the second element are the target label. Returns: - A tuple (x, y). The element x is a tuple containing (1) a tensor of padded - word vectors and (2) their respective original sequence lengths. The element - y is a tensor of padded tag indices. The word vectors are padded with vectors - of 0s and the tag indices are padded with -100s. Padding with -100 is done - because of the cross-entropy loss and the accuracy metric ignores + A tuple (x, y). The element x is a tuple containing (1) a tensor of padded + word vectors and (2) their respective original sequence lengths. The element + y is a tensor of padded tag indices. The word vectors are padded with vectors + of 0s and the tag indices are padded with -100s. Padding with -100 is done + because of the cross-entropy loss and the accuracy metric ignores the targets with values -100. """ - # This gets us two lists of tensors and a list of integer. + # This gets us two lists of tensors and a list of integer. # Each tensor in the first list is a sequence of word vectors. # Each tensor in the second list is a sequence of tag indices. # The list of integer consist of the lengths of the sequences in order. sequences_vectors, sequences_labels, lengths = zip(*[ - (torch.FloatTensor(seq_vectors), torch.LongTensor(labels), len(seq_vectors)) + (torch.FloatTensor(seq_vectors), torch.LongTensor(labels), len(seq_vectors)) for (seq_vectors, labels) in sorted(batch, key=lambda x: len(x[0]), reverse=True) ]) @@ -352,7 +355,8 @@ test_loader = DataLoader(test_dataset_vectorizer, batch_size=batch_size, collate ``` ## Full Network -> We use a second trick, ``packing``. + +> We use a second trick, `packing`. Since our sequences are of variable lengths and that we want to be as efficient as possible when packing them, we cannot use the [PyTorch `nn.Sequential`](https://pytorch.org/docs/stable/generated/torch.nn.Sequential.html) class to define our model. Instead, we define the forward pass so that it uses packed sequences (again, you can read [this good explanation](https://stackoverflow.com/questions/51030782/why-do-we-pack-the-sequences-in-pytorch) on why we pack sequences). @@ -361,10 +365,10 @@ class RecurrentNet(nn.Module): def __init__(self, lstm_network, fully_connected_network): super().__init__() self.hidden_state = None - + self.lstm_network = lstm_network self.fully_connected_network = fully_connected_network - + def forward(self, padded_sequences_vectors, lengths): """ Defines the computation performed at every call. @@ -389,15 +393,14 @@ full_network = RecurrentNet(lstm_network, fully_connected_network) ## Summary We have created an LSTM network (`lstm_network`) and a fully connected network (`fully_connected_network`), and we use both -components in the full network. The full network makes use of padded-packed sequences, -so we created the `pad_collate_fn` function to do the necessary work within the `DataLoader`. Finally, -we will load the data using the vectorizer (within the `DataLoader` using the `pad_collate` function). This means that the addresses will be represented by word embeddings. +components in the full network. The full network makes use of padded-packed sequences, +so we created the `pad_collate_fn` function to do the necessary work within the `DataLoader`. Finally, +we will load the data using the vectorizer (within the `DataLoader` using the `pad_collate` function). This means that the addresses will be represented by word embeddings. Also, the address components will be converted into categorical value (from 0 to 7). - ## The Training -Now that we have all the components for the network, let's define our optimizer (Stochastic Gradient Descent) +Now that we have all the components for the network, let's define our optimizer (Stochastic Gradient Descent) ([SGD](https://en.wikipedia.org/wiki/Stochastic_gradient_descent)). ```python @@ -405,6 +408,7 @@ optimizer = optim.SGD(full_network.parameters(), lr) ``` ### Poutyne Experiment + > Disclaimer: David is a developer on the Poutyne library, so we will present code using this framework. See the project [here](https://poutyne.org/). Let's create our experiment using Poutyne for automated logging in the project root directory (`./`). We will also set @@ -420,9 +424,11 @@ Using our experiment, we can now launch the training as simply as ```python exp.train(train_loader, valid_generator=valid_loader, epochs=epoch_number) ``` + It will take around 40 minutes per epochs, so a couple hours for the complete training. ### Results + The next figure shows the loss and the accuracy during our training (blue) and during our validation (orange) steps. After 10 epochs, we obtain a validation loss and accuracy of `0.01981` and `99.54701` respectively, satisfying values for a first model. Also, since our training accuracy and loss closely match their respective validation values, our model does not appear to be overfitted on the training set. @@ -433,7 +439,7 @@ After 10 epochs, we obtain a validation loss and accuracy of `0.01981` and `99.5 It seems that our model performed pretty well, but just for fun, let's unleash the full potential of LSTMs using a bidirectional approach (bidirectional LSTM). What it means is that instead of _simply_ viewing the sequence from the start to the end, we also train the model to see the sequence from the end to the start. It's important to state that the two directions are -not shared, meaning that the model _sees_ the sequence in one direction at the time, but gathers the information from both directions into the +not shared, meaning that the model _sees_ the sequence in one direction at the time, but gathers the information from both directions into the fully connected layer. That way, our model can get insight from both directions. Instead of using only one layer, let's use a bidirectional bi-LSTM, which means that we use two layers of hidden state for each direction. @@ -457,9 +463,9 @@ fully_connected_network = nn.Linear(input_dim, tag_dimension) full_network_bi_lstm = RecurrentNet(lstm_network, fully_connected_network) ``` - + ### Training - + ```python exp_bi_lstm = Experiment("./", full_network_bi_lstm, device=device, optimizer=optimizer, loss_function=cross_entropy, batch_metrics=["acc"]) @@ -467,13 +473,14 @@ exp_bi_lstm.train(train_loader, valid_generator=valid_loader, epochs=epoch_numbe ``` ### Results + Here are our validation results for the last epoch of the larger model. On the validation dataset, we can see that we obtain a marginal gain of around 0.3% for the accuracy over our previous simpler model. This is only a slight improvement. -| Model | Bidirectional bi-LSTM | -|:--------:|:------------------:| -| Loss | 0.0050 | -| Accuracy | 99.8594 | +| Model | Bidirectional bi-LSTM | +| :------: | :-------------------: | +| Loss | 0.0050 | +| Accuracy | 99.8594 | But now that we have our two trained models, let's use the test set as a final and **unique** step for evaluating their performance. @@ -484,28 +491,29 @@ exp_bi_lstm.test(test_loader) The next table presents the results of the bidirectional bi-LSTM with two layers and the previous model (LSTM with one layer). -| Model | LSTM one layer | Bidirectional bi-LSTM | -|:--------:|:--------------:|:------------------:| -| Loss | 0.0152 | **0.0050** | -| Accuracy | 99.5758 | **99.8550** | +| Model | LSTM one layer | Bidirectional bi-LSTM | +| :------: | :------------: | :-------------------: | +| Loss | 0.0152 | **0.0050** | +| Accuracy | 99.5758 | **99.8550** | We see similar validation results for both models. Also, we still see a little improvement in accuracy and total loss for the larger model. Considering that we only improved by around 0.3%, one can argue that the difference is only due to training variance (mostly due to our random sampling of training batches). To test the robustness of our approach, we could train our model multiple times -using different random seeds and report the mean and standard deviation of each metric over all experiments rather than the result of a single training. Let's try something else. +using different random seeds and report the mean and standard deviation of each metric over all experiments rather than the result of a single training. Let's try something else. ### Zero Shot Evaluation + Since we have at our disposition addresses from other countries, let's see if our model has really learned a typical address sequence -or if it has simply *memorized* all the training examples. +or if it has simply _memorized_ all the training examples. We will test our model on three different types of dataset - - first, on addresses with the exact same structure - as in our training dataset: addresses from the United States of America (US) and the United Kingdom (UK) - - secondly, on addresses with the exact same structure as those in our training dataset **but** - written in a totally different language: addresses from Russia (RU) - - finally, on addresses that exhibit a different structure **and** that are written in a different language: addresses from Mexico (MX). +- first, on addresses with the exact same structure + as in our training dataset: addresses from the United States of America (US) and the United Kingdom (UK) +- secondly, on addresses with the exact same structure as those in our training dataset **but** + written in a totally different language: addresses from Russia (RU) +- finally, on addresses that exhibit a different structure **and** that are written in a different language: addresses from Mexico (MX). -For each test, we will use a dataset of `100,000` examples in total, and we will evaluate using the best epoch of our two models (i.e. last epoch for both of them). +For each test, we will use a dataset of `100,000` examples in total, and we will evaluate using the best epoch of our two models (i.e. last epoch for both of them). Also, we will use the same pre-processing steps as before (i.e. data vectorization, the same pad collate function), but we will only apply a test phase, meaning no training step. @@ -543,22 +551,21 @@ exp_bi_lstm.test(gb_loader) ``` The next table presents the results of both models for both countries. We obtain -better results for the two countries using the bidirectional bi-LSTM (around 8% better). It's interesting to see that, considering address structures are similar to those in the training dataset (Canada), we obtain near as good results as those observed during training. This suggests that our model seems to have learned to recognize the structure of an address. Also, despite the language being the same as in the training dataset (i.e. some English addresses in the bilingual canadian address dataset), we obtain poorer results. That situation is most likely due to the fact that the postal code formats are not the same. For the US, it is 5 digits, and for the UK it is similar to that of Canada, but it is not always a letter followed by a number and not always 6 characters. It is *normal* for a model to +better results for the two countries using the bidirectional bi-LSTM (around 8% better). It's interesting to see that, considering address structures are similar to those in the training dataset (Canada), we obtain near as good results as those observed during training. This suggests that our model seems to have learned to recognize the structure of an address. Also, despite the language being the same as in the training dataset (i.e. some English addresses in the bilingual canadian address dataset), we obtain poorer results. That situation is most likely due to the fact that the postal code formats are not the same. For the US, it is 5 digits, and for the UK it is similar to that of Canada, but it is not always a letter followed by a number and not always 6 characters. It is _normal_ for a model to have difficulty when faced with new patterns. All in all, we can say that our model has achieved good results. | Model (Country) | LSTM one layer | Bidirectional bi-LSTM | -|:---------------:|:--------------:|:------------------:| -| Loss (US) | 0.6176 | **0.3078** | -| Accuracy (US) | 84.7396 | **91.8220** | -| Loss (UK) | 0.4368 | **0.1571** | -| Accuracy (UK) | 86.2543 | **95.6840** | - +| :-------------: | :------------: | :-------------------: | +| Loss (US) | 0.6176 | **0.3078** | +| Accuracy (US) | 84.7396 | **91.8220** | +| Loss (UK) | 0.4368 | **0.1571** | +| Accuracy (UK) | 86.2543 | **95.6840** | ##### The Second and Third Test -Now let's test for Russia and Mexico. +Now let's test for Russia and Mexico. -But first, let's discuss how our French embeddings can generate word vectors for vocabulary in a different language. FastText uses subword embeddings when complete embeddings do not exist. For example, we can assume the presence of a word embedding vector for the word `Roi`, but we face an out-of-vocabulary (OOV) for the word `H1A1` since this word is not a real word. The trick with fastText is that it creates composite embeddings using the subword with fixed window size (length of the subword) when facing OOV words. For example, a two characters window embeddings of `H1A1` would be the aggregated embeddings of the subword `H1`, `1A` and `A1`. +But first, let's discuss how our French embeddings can generate word vectors for vocabulary in a different language. FastText uses subword embeddings when complete embeddings do not exist. For example, we can assume the presence of a word embedding vector for the word `Roi`, but we face an out-of-vocabulary (OOV) for the word `H1A1` since this word is not a real word. The trick with fastText is that it creates composite embeddings using the subword with fixed window size (length of the subword) when facing OOV words. For example, a two characters window embeddings of `H1A1` would be the aggregated embeddings of the subword `H1`, `1A` and `A1`. ```python ru_loader = DataLoader(ru_data, batch_size=batch_size, collate_fn=pad_collate_fn) @@ -570,28 +577,29 @@ exp.test(mx_loader) exp_bi_lstm.test(mx_loader) ``` -The next table presents the results of both models for the two countries tested. We see that the first test -(RU) gives poorer results than those for Mexican addresses, even if these latter are written in a different structure and language. This situation could be explained by both languages' roots; Spanish is closer to French than Russian is. -An interesting thing is that even in a *difficult* annotation context, both models perform relatively well. -It suggests that our models have really learned the *logic* of an address sequence. It could also mean that, if +The next table presents the results of both models for the two countries tested. We see that the first test +(RU) gives poorer results than those for Mexican addresses, even if these latter are written in a different structure and language. This situation could be explained by both languages' roots; Spanish is closer to French than Russian is. +An interesting thing is that even in a _difficult_ annotation context, both models perform relatively well. +It suggests that our models have really learned the _logic_ of an address sequence. It could also mean that, if we train our model longer, we could potentially improve our results. Other modifications that could improve our models are discussed in the next and final section. | Model (Country) | LSTM one layer | Bidirectional bi-LSTM | -|:---------------:|:--------------:|:------------------:| -| Loss (RU) | **2.5181** | 4.6118 | -| Accuracy (RU) | **48.9820** | 47.3185 | -| Loss (MX) | 2.6786 | **1.7147** | -| Accuracy (MX) | 50.2013 | **63.5317** | +| :-------------: | :------------: | :-------------------: | +| Loss (RU) | **2.5181** | 4.6118 | +| Accuracy (RU) | **48.9820** | 47.3185 | +| Loss (MX) | 2.6786 | **1.7147** | +| Accuracy (MX) | 50.2013 | **63.5317** | ### Summary + In summary, we found that using a bidirectional bi-LSTM seems to perform better on addresses not seen during training, including those coming from other countries. Still, the results for addresses from other countries are not as good as those for Canadian addresses (training dataset). A solution to this problem could be to train a model using all the -data from all over the world. This approach was used by [Libpostal](https://github.com/openvenues/libpostal), which trained a +data from all over the world. This approach was used by [Libpostal](https://github.com/openvenues/libpostal), which trained a CRF over an impressive near `100` million addresses (yes, **100 million**). If you want to explore this avenue, the data they used is publicly available [here](https://github.com/openvenues/libpostal). -We also explored the idea that the language disparity has a negative impact on the results, since we use monolingual word embeddings (i.e. French), which is *normal* considering that they were trained for a specific language. +We also explored the idea that the language disparity has a negative impact on the results, since we use monolingual word embeddings (i.e. French), which is _normal_ considering that they were trained for a specific language. > Alert of self-promotion of our work here. -We've personally explored this avenue in an article using [subword embedding for address parsing](https://arxiv.org/abs/2006.16152) and we've release our trained models [here](https://deepparse.org/). +> We've personally explored this avenue in an article using [subword embedding for address parsing](https://arxiv.org/abs/2006.16152) and we've release our trained models [here](https://deepparse.org/). That being said, our model still performed well on the Canadian dataset, and one can simply train simpler LSTM model using -country data to obtain the best results possible with a model as simple as possible. +country data to obtain the best results possible with a model as simple as possible. diff --git a/content/blog/2020-10-30-reproducibility-in-ml-a-talk/index.fr.md b/content/blog/2020-10-30-reproducibility-in-ml-a-talk/index.fr.md index c5ae1ec83..8f293dee9 100644 --- a/content/blog/2020-10-30-reproducibility-in-ml-a-talk/index.fr.md +++ b/content/blog/2020-10-30-reproducibility-in-ml-a-talk/index.fr.md @@ -2,17 +2,17 @@ title: Reproductibilité en apprentissage automatique - Webinaire author: David Beauchemin date: '2020-11-11' -slug: machine learning +slug: reproductibilite-apprentissage-automatique type: post -categories: ["Nouvelles", "Machine learning"] +categories: ['Nouvelles', 'Machine learning'] tags: [] -description: "Des solutions aux embûches à la reproductibilité en apprentissage automatique" -featured: "hackathon-code-cover.JPG" -featuredpath: "img/headers/" +description: 'Des solutions aux embûches à la reproductibilité en apprentissage automatique' +featured: 'hackathon-code-cover.JPG' +featuredpath: 'img/headers/' --- > Un article rapide sur des solutions aux embûches à la reproductibilité en apprentissage automatique. -Récemment, j'ai offert un webinaire auprès de l'Institut intelligence et données (IID) de l'Université Laval sur la reproductibilité en apprentissage automatique. Le sujet m'interpelle puisqu'il a un impact important sur la crédibilité de nos résultats de recherche et sur notre productivité. Voici donc [l'enregistrement](https://www.youtube.com/watch?v=Fw_lRiTrmnk&feature=youtu.be) de ma présentation et le [contenu](https://davebulaval.github.io/reproductibilite-en-apprentissage-automatique/) de celle-ci. +Récemment, j'ai offert un webinaire auprès de l'Institut intelligence et données (IID) de l'Université Laval sur la reproductibilité en apprentissage automatique. Le sujet m'interpelle puisqu'il a un impact important sur la crédibilité de nos résultats de recherche et sur notre productivité. Voici donc [l'enregistrement](https://www.youtube.com/watch?v=Fw_lRiTrmnk&feature=youtu.be) de ma présentation et le [contenu](https://davebulaval.github.io/reproductibilite-en-apprentissage-automatique/) de celle-ci. En espérant que les outils présentés vous aideront à rendre vos solutions plus robustes et qu'ils amélioreront votre productivité. From e99cde2e6dd54733e2a51c83e98a75b564501bce Mon Sep 17 00:00:00 2001 From: Annie Deshaies Date: Thu, 4 Mar 2021 11:23:48 -0500 Subject: [PATCH 2/7] aliases --- .../index.en.md | 39 +-- .../index.en.md | 35 +-- .../index.fr.md | 42 +-- .../index.fr.md | 54 ++-- .../index.fr.md | 41 +-- .../2018-11-17-stein-baseball/index.en.md | 85 +++--- .../index.en.md | 36 +-- .../index.fr.md | 37 +-- .../2019-05-21-argparse-package/index.fr.Rmd | 1 + .../2019-05-21-argparse-package/index.fr.html | 241 +++++++++++++----- .../index.fr.md | 1 + .../blog/2019-12-19-recap-2019/index.fr.md | 72 +++--- .../index.fr.md | 95 ++++--- .../index.en.md | 41 +-- content/blog/2020-03-19-howto/index.en.md | 45 ++-- content/blog/2020-03-19-howto/index.fr.md | 47 ++-- content/blog/2020-06-09/index.fr.md | 27 +- .../2020-07-29-openlayer-one-year/index.fr.md | 1 + .../index.en.md | 1 + .../index.fr.md | 5 +- .../2021-02-01-tidynhl-analyse/index.fr.md | 153 +++++------ .../blog/2021-02-25-recap-2020/index.fr.md | 35 ++- 22 files changed, 608 insertions(+), 526 deletions(-) diff --git a/content/blog/2018-09-01-grammar-of-graphics/index.en.md b/content/blog/2018-09-01-grammar-of-graphics/index.en.md index c43bf908e..af2bcd6e8 100644 --- a/content/blog/2018-09-01-grammar-of-graphics/index.en.md +++ b/content/blog/2018-09-01-grammar-of-graphics/index.en.md @@ -2,21 +2,20 @@ title: The grammar of graphics slug: grammar-of-graphics author: Stéphane Caron -description: "The art of communicating with graphics" +description: 'The art of communicating with graphics' date: '2018-09-01' -categories: ["R"] +categories: ['R'] type: post -featured: "ggplot-cover.jpg" -featuredpath: "img/headers/" -tags: ["Vizualisation", "Plot", "Ggplot2"] +featured: 'ggplot-cover.jpg' +featuredpath: 'img/headers/' +tags: ['Vizualisation', 'Plot', 'Ggplot2'] output: html_document: keep_md: true +aliases: [/blog/2018-09-01-grammar-of-graphics/grammar-of-graphics/] --- - - -Data science is a growing field that regroups talented and passionate people who generally show remarkable technical skills and outstanding ease to solve problems. However, from my personnal experience, one particular skill is often undervalued: **Communication**. In data science, as in many other fields, graphical vizualisation is an important tool that helps to simplify and clearly communicate results to others. For that reason, it's crucial to master this set of skills. Just like any other means of communcation, like speaking or writing, graphical vizualisation basically requires two main components: +Data science is a growing field that regroups talented and passionate people who generally show remarkable technical skills and outstanding ease to solve problems. However, from my personnal experience, one particular skill is often undervalued: **Communication**. In data science, as in many other fields, graphical vizualisation is an important tool that helps to simplify and clearly communicate results to others. For that reason, it's crucial to master this set of skills. Just like any other means of communcation, like speaking or writing, graphical vizualisation basically requires two main components: - A synthax/grammar/set of rules that ensures everything is well organized and broadly correct. - A feeling/intuition to communicate the right things and present them in a way that will make people feel what you want them to feel. @@ -46,8 +45,7 @@ In its simple form, any well-constructed sentence requires at least mininal elem #### Data -The data component is nothing but the dataset behind the graph. Without it, there is nothing to be plotted or vizualised. In order to properly use the `ggplot2` framework and the grammar of graphics, the data structure is quite important. The data structure often related to this matter refers to the [tidy](https://cran.r-project.org/web/packages/tidyr/vignettes/tidy-data.html) format. For our example, we'll use the `mtcars` dataset, from the [`dataset`](https://stat.ethz.ch/R-manual/R-devel/library/datasets/html/mtcars.html) library. This particular dataset is already in a tidy (one-observation-one-row) structure as we can see below. - +The data component is nothing but the dataset behind the graph. Without it, there is nothing to be plotted or vizualised. In order to properly use the `ggplot2` framework and the grammar of graphics, the data structure is quite important. The data structure often related to this matter refers to the [tidy](https://cran.r-project.org/web/packages/tidyr/vignettes/tidy-data.html) format. For our example, we'll use the `mtcars` dataset, from the [`dataset`](https://stat.ethz.ch/R-manual/R-devel/library/datasets/html/mtcars.html) library. This particular dataset is already in a tidy (one-observation-one-row) structure as we can see below. ```r library(ggplot2) @@ -66,7 +64,6 @@ head(mtcars) We can see our data layer as a bank of words we could use to build our sentence. Once we have our available words, obviously, we need to pick up few of them and organize them in order to build a sentence. Aesthetics is somewhat similar to the action of "pick and organize"" as it defines the scales onto which the data selected is mapped. For example, we could decide to select horsepower and miles per gallon variables from our dataset and plot them onto x and y axis respectively ([see figure below](#fig:aes)). - ```r ggplot(mtcars, aes(x = hp, y = mpg)) + geom_point() @@ -77,17 +74,16 @@ ggplot(mtcars, aes(x = hp, y = mpg)) +

Aesthetics (hp and mpg) define which scales are plotted.

-In the `ggplot2` framework, we define our aesthetics in the aes() argument. +In the `ggplot2` framework, we define our aesthetics in the aes() argument. #### Geometries In the previous section, we use points through a scatter plot to vizualise our data. The use of points, or other visual elements known as geometries (lines, bars, text, etc) control the type of plot you wanna create. As a comparison, it can be seen as the type of sentence (declarative, imperative, interrogative, etc) you wanna build. It does not define your content, but rather the way you express your content. For example, instead of points, we could have shown bars ([see figure below](#fig:geom)). - ```r -mtcars %>% - group_by(cyl) %>% - summarise(hp_mean = mean(hp)) %>% +mtcars %>% + group_by(cyl) %>% + summarise(hp_mean = mean(hp)) %>% ggplot(aes(x = cyl, y = hp_mean)) + geom_bar(stat = "identity") ``` @@ -114,7 +110,6 @@ Facetting a graphic means to plot small different subsets of the data. Relating For example, it's difficult to see the true pattern of each group in the following [graphic](#fig:facet1): - ```r ggplot(mtcars, aes(x = hp, y = mpg, color = as.factor(carb))) + geom_point() @@ -145,7 +140,6 @@ Statistics is a layer that summarizes the data in order to aid our understanding In our example, it's far more difficult to draw conclusions about the distributions of each number of cylinders from the [figure](#fig:boxplot1) - ```r ggplot(mtcars, aes(x = as.factor(cyl), y = mpg)) + geom_point() @@ -158,7 +152,6 @@ ggplot(mtcars, aes(x = as.factor(cyl), y = mpg)) + compared to the [figure below](#fig:bloxplot2), where we can easily extract the median, the quartiles and so on... - ```r ggplot(mtcars, aes(x = as.factor(cyl), y = mpg)) + geom_boxplot() @@ -177,14 +170,13 @@ Coordinates define the space in which we plot our data. Usually, the Cartesian c Themes can be seen as evertything non-related to the data. In the grammar of graphics framework, themes ease the understanding of our plots, not necessarily making them more beautiful. In fact, the font type and size, the spacing, the margins, and so on, help to make a text more understandable for the reader. In the same way, the size of geoms, the grid lines or the background color should be carefully chosen in order to direct the audience's focus on the message we want to express with our plot. For example, the non-related data elements in the [figure](#fig:themes1) make it difficult to see relevant patterns in the trend shown by the data: - ```r ggplot(mtcars, aes(x = mpg, y = qsec)) + geom_point(color = "yellow", size = 2) + geom_smooth(color = "red") + labs(title = "Qsec vs mpg") + theme_dark() + - theme(title = element_text(family = "Luminari", face = "bold.italic", colour = "red"), + theme(title = element_text(family = "Luminari", face = "bold.italic", colour = "red"), plot.background = element_rect(fill = "yellow")) ``` @@ -195,7 +187,6 @@ ggplot(mtcars, aes(x = mpg, y = qsec)) + In contrast, the next [figure](#fig:theme2) is way clearer as nothing irrelevant has been added to the plot. This leads the reader to focus on the data and the trend behind it: - ```r ggplot(mtcars, aes(x = mpg, y = qsec)) + geom_point() + @@ -213,7 +204,7 @@ In `ggplot2` framework, some themes are prebuilt and can be used directly. Howev # Intuition for good graphics -As mentionned in the introduction, a good and structured communication requires to follow certain rules (which were described in the first part), but also requires a kind of feeling to **identify** the things that need to be communicated and to make good choices about **how** to communicate them. +As mentionned in the introduction, a good and structured communication requires to follow certain rules (which were described in the first part), but also requires a kind of feeling to **identify** the things that need to be communicated and to make good choices about **how** to communicate them. > "Good grammar is just the first step in creating a good sentence."[^hadley] @@ -245,7 +236,6 @@ The [example 1 figure](#fig:example1) is an example of graphic that basically me The last example shows that there exists multiple ways to build a given graphical representation. However, all those representations can be "sorted" by relevance in a **given situation**. That being said, I think that for each context, depending on different elements such as the objective, the data and the audience, there exists a particular representation that is the most appropriate for **that particular situation**. Then, there are multiple representations that are still good, which means that the objective is then to practice and gain experience to ultimately approach the most suited one. - [^hadley]: Hadley Wickham # Conclusion @@ -258,4 +248,3 @@ The complete code of this article can be found through this [link](https://githu - https://thenextscoop.com/balance-visual-communication-relying-words/ - https://skillgaze.com/2017/10/31/understanding-different-visualization-layers-of-ggplot/ - diff --git a/content/blog/2018-09-16-our-day-at-otthac18/index.en.md b/content/blog/2018-09-16-our-day-at-otthac18/index.en.md index 46d5a3d5a..244364542 100644 --- a/content/blog/2018-09-16-our-day-at-otthac18/index.en.md +++ b/content/blog/2018-09-16-our-day-at-otthac18/index.en.md @@ -4,28 +4,27 @@ author: dot-hockey #Philippe Blouin-Leclerc; Antoine Buteau; Stéphane Caron; Sa date: '2018-09-16' slug: our-day-at-otthac18 type: post -categories: ["Analytics"] -tags: ["Hockey Analytics"] -description: "" -featured: "otthac18-cover.png" -featuredpath: "img/headers/" +categories: ['Analytics'] +tags: ['Hockey Analytics'] +description: '' +featured: 'otthac18-cover.png' +featuredpath: 'img/headers/' +aliases: [/blog/2018-09-16-our-day-at-otthac18/our-day-at-otthac18/] --- -For any die-hard hockey fan, September 13th will be remembered as the day Erik Karlsson was traded by the Sens, and not without controversy, as it seems Ottawa got less for Karlsson than they gave to acquire Duchene earlier this year. It turns out that the 4th Annual Ottawa International Hockey Analytics Conference (OTTHAC18), again held at Carleton University, was scheduled on the following weekend (September 14th and 15th, 2018). We couldn't get there for the workshop and activities of Friday, but made the trip on Saturday to get a glimpse of what is going on in the hockey analytics community and, of course, hear what the folks in Ottawa thought about the most recent trade activity in the NHL. The day was packed with presentations, interviews and panels. Notable speakers and attendees were, among others, Rob Vollman (author of the book *Stat Shot*), Micah Blake McCurdy ([hockeyviz.com](hockeyviz.com)) and Elias Collette (consultant for the Ottawa Senators). You can see the full schedule by following this [link](http://statsportsconsulting.com/main/wp-content/uploads/OTTHAC18Schedule_0907.pdf). Here are the highlights of our day (in no particular order). - +For any die-hard hockey fan, September 13th will be remembered as the day Erik Karlsson was traded by the Sens, and not without controversy, as it seems Ottawa got less for Karlsson than they gave to acquire Duchene earlier this year. It turns out that the 4th Annual Ottawa International Hockey Analytics Conference (OTTHAC18), again held at Carleton University, was scheduled on the following weekend (September 14th and 15th, 2018). We couldn't get there for the workshop and activities of Friday, but made the trip on Saturday to get a glimpse of what is going on in the hockey analytics community and, of course, hear what the folks in Ottawa thought about the most recent trade activity in the NHL. The day was packed with presentations, interviews and panels. Notable speakers and attendees were, among others, Rob Vollman (author of the book _Stat Shot_), Micah Blake McCurdy ([hockeyviz.com](hockeyviz.com)) and Elias Collette (consultant for the Ottawa Senators). You can see the full schedule by following this [link](http://statsportsconsulting.com/main/wp-content/uploads/OTTHAC18Schedule_0907.pdf). Here are the highlights of our day (in no particular order). ## What if Oscar Klefbom didn't get injured? -Tyrel Stokes' (McGill University) presentation, *Estimating the Causal Effect of Injury on Performance*, focused on the question: What would the world look like if someone wouldn't have gotten injured? And what are the consequences of getting injured? His prime example was Oscar Klefbom, who missed 52 games during the 2015-2016 season. Klefbom is a great example because he got injured during his second season in the NHL: if you compare Klefbom's stats from before and after his injury, it seems that he got better. The injury made him better? Of course not. Any serious analysis needs to take into account that Klefbom got older, stronger, more confident, and so on. It is interesting however to try to quantify the impact of Klefbom's injury on his progression? For the stats amateurs, the idea behind Tyrel's analysis is to model the progression of players using a random walk (with potential jumps). His conclusions are that injuries do indeed have a large short-term effect, but a small long-term effect. I guess that's a good thing! - -Speaking of *what ifs*, what if Lemieux didn't get injured? Tyrel doesn't answer that question, but has lots to say about the comparison between Lemieux and Gretzky. If you're interested, check out his blog [Stats by Stokes](https://statsbystokes.wordpress.com). +Tyrel Stokes' (McGill University) presentation, _Estimating the Causal Effect of Injury on Performance_, focused on the question: What would the world look like if someone wouldn't have gotten injured? And what are the consequences of getting injured? His prime example was Oscar Klefbom, who missed 52 games during the 2015-2016 season. Klefbom is a great example because he got injured during his second season in the NHL: if you compare Klefbom's stats from before and after his injury, it seems that he got better. The injury made him better? Of course not. Any serious analysis needs to take into account that Klefbom got older, stronger, more confident, and so on. It is interesting however to try to quantify the impact of Klefbom's injury on his progression? For the stats amateurs, the idea behind Tyrel's analysis is to model the progression of players using a random walk (with potential jumps). His conclusions are that injuries do indeed have a large short-term effect, but a small long-term effect. I guess that's a good thing! +Speaking of _what ifs_, what if Lemieux didn't get injured? Tyrel doesn't answer that question, but has lots to say about the comparison between Lemieux and Gretzky. If you're interested, check out his blog [Stats by Stokes](https://statsbystokes.wordpress.com). ## The rise of artificial intelligence It's hard to have a conversation about AI in hockey (and sports in general) without mentionning the Montreal-based company SPORTLOGiQ. In their own words: "our software is able to transform raw video data into advanced sports analytics by flagging specific game events—shots, passes and possessions—and recording their XY coordinates." -We were happy to see that the sixth presentation of the day was given by someone, Campbell Weaver (Princeton University), who's building his own player tracking algorithm. He (or his algorithm) tracks the players' movements on the ice (from a broadcast video), using Keras and *deep convolution neural networks*. To circumvent the fact that it's hard to get a ton of labeled video (to teach the algorithm), he took advantage of the famous ImageNet dataset: in short, he first trained his algorithm to recognize everyday life things and only then began to teach him some hockey stuff. Why would this help? +We were happy to see that the sixth presentation of the day was given by someone, Campbell Weaver (Princeton University), who's building his own player tracking algorithm. He (or his algorithm) tracks the players' movements on the ice (from a broadcast video), using Keras and _deep convolution neural networks_. To circumvent the fact that it's hard to get a ton of labeled video (to teach the algorithm), he took advantage of the famous ImageNet dataset: in short, he first trained his algorithm to recognize everyday life things and only then began to teach him some hockey stuff. Why would this help? @@ -34,16 +33,13 @@ Apart from the actual tracking of the players on the ice, Campbell also introduc Campbell's now beginning a master's degree in artificial intelligence, so there's no guarantee that he will find the time to advance his project as much as he would like in the next year or so, but check out his github repo https://github.com/ccweaver1/bsi_vision to see how the project is evolving. - - ## Speaking of SPORTLOGiQ... -There was one (or more) representative from SPORTLOGiQ, David Yu, who presented a very nice poster titled *Analysis of team level pace of play in hockey using spatio-temporal data*. None of us have seen a clearer analysis of the pace of play in hockey than this. Have a look for yourself... +There was one (or more) representative from SPORTLOGiQ, David Yu, who presented a very nice poster titled _Analysis of team level pace of play in hockey using spatio-temporal data_. None of us have seen a clearer analysis of the pace of play in hockey than this. Have a look for yourself... - ## Hockey in space

@@ -55,7 +51,6 @@ There was one (or more) representative from SPORTLOGiQ, David Yu, who presented To make your own comparisons, check out his very nice [Shiny app](https://dbecker.shinyapps.io/LGCP_Results/) (built using the more-than-cool R package [Shiny](https://shiny.rstudio.com/)). To get the deviation from league average, choose the option S(x,y) in the Data scrolling menu. A nice viz tool, useful to get some quick insights about other teams tendencies, strategies, weaknesses, etc. Interesting! :clap: :clap: - ## Malkin vs McDavid In the new age of data, it's all about context! Indeed, we can now analyze the basic statistics collected such as shots, goals, assists, etc, focusing on very specific contexts. The idea of normalizing with respect to context was the basis of Micah Black McCurdy's talk: [Isolating Individual Skater Impact on Team Shot Quantity and Quality](http://hockeyviz.com/static/pdf/otthac18.pdf). @@ -67,18 +62,14 @@ To illustrate the idea, imagine you want to compare Evegni Malkin and Connor McD - score impact: playing in a team that always trail or not? - zone impact: playing most of his time in offensive or defensive zones? -We won't go into the technical details of his method; you can find them by following the (above) link to his presentation. We also strongly encourage you to take a glimpse at his results (slides 22-23 and 32-37). You may be surprised to see that Brendan Gallagher is rated quite well, and that Sidney Crosby is not the best 5v5 net performer in the league (according to the metric!)... :anguished: :anguished: :anguished: - +We won't go into the technical details of his method; you can find them by following the (above) link to his presentation. We also strongly encourage you to take a glimpse at his results (slides 22-23 and 32-37). You may be surprised to see that Brendan Gallagher is rated quite well, and that Sidney Crosby is not the best 5v5 net performer in the league (according to the metric!)... :anguished: :anguished: :anguished: ## Final word -Of course, this was just a small part of what happened on Saturday, and we're sorry we couldn't summarize it all because most of it was pretty interesting. Omitted (but still worthy!) presentations treated the questions of how to best qualify/quantify the pace of play (Tim Swartz); how to identify exceptional players (Yejia Liu, Simon Fraser University); concussions and dementia (Lili Hazrati, The Hospital for Sick Children); and the hot hand theory in hockey (Likang Ding). +Of course, this was just a small part of what happened on Saturday, and we're sorry we couldn't summarize it all because most of it was pretty interesting. Omitted (but still worthy!) presentations treated the questions of how to best qualify/quantify the pace of play (Tim Swartz); how to identify exceptional players (Yejia Liu, Simon Fraser University); concussions and dementia (Lili Hazrati, The Hospital for Sick Children); and the hot hand theory in hockey (Likang Ding). Other posters, which were all very original, were about improving the plus/minus statistic (Seongwon Im, we think this is a great idea); applying the Elo ratings from chess to hockey (Roman Parparov); the effect of aging on the players' performances, as well as the effect of end-of-contract (Kyle Stich); and finally, creating 3D models for the distribution of shot locations (University of Toronto Hockey Analytics Club). All slides and posters are available [here](http://statsportsconsulting.com/otthac18/). There were also multiple panels/interviews with hockey experts. This is where the Karlsson saga popped all the time. Panelists/experts that identified themselves as Sens fans appeared quite disappointed (to say the least), but most of them seemed to agree on one thing: it's time for a rebuilding phase in Ottawa: invest in young players and show patience. The only problem is that Ottawa gave away their first-round pick for the 2019 draft in the Duchesne trade (to Colorado). Chances are that Joe Sakic was smiling when he heard about the Karlsson trade... - - - diff --git a/content/blog/2018-11-17-hackathon-winners-code/index.fr.md b/content/blog/2018-11-17-hackathon-winners-code/index.fr.md index 9c5dafa63..68525fc75 100644 --- a/content/blog/2018-11-17-hackathon-winners-code/index.fr.md +++ b/content/blog/2018-11-17-hackathon-winners-code/index.fr.md @@ -1,19 +1,20 @@ --- title: Entrevue avec les gagnants -author: "Stéphane Caron et Marc-André Bernier" +author: 'Stéphane Caron et Marc-André Bernier' date: '2018-12-02' slug: hackathon-winners-code type: post -categories: ["Hackathon"] -tags: ["Interview", "Meetup", "Hackathon"] -description: "Meetup ML Québec | Prix Linus Torvalds" -featured: "hackathon-code-cover.JPG" -featuredpath: "img/headers/" +categories: ['Hackathon'] +tags: ['Interview', 'Meetup', 'Hackathon'] +description: 'Meetup ML Québec | Prix Linus Torvalds' +featured: 'hackathon-code-cover.JPG' +featuredpath: 'img/headers/' +aliases: [/blog/2018-11-17-hackathon-winners-code/hackathon-winners-code/] --- Le 10 novembre 2018 avait lieu à Québec la [Journée hackathon en assurance](https://www.facebook.com/events/185652975580020/), organisée par [MeetupMLQuebec](https://www.facebook.com/MeetupMLQuebec) et présentée en collaboration par Intact Assurances et Co-operators. -Félicitations à *Last but not furious*, gagnants du prix *Linus Torvalds*, remis à l'équipe qui a remis le code de meilleure qualité. Les juges avaient notamment à l'oeil les critères suivants: +Félicitations à _Last but not furious_, gagnants du prix _Linus Torvalds_, remis à l'équipe qui a remis le code de meilleure qualité. Les juges avaient notamment à l'oeil les critères suivants: - La structure générale du projet - L'indentation utilisée dans les programmes @@ -33,13 +34,13 @@ R: Certains d'entre nous avions déjà participé à des compétitions dans le m #### Q: Discutons maintenant de ce qui vous a fait gagner le prix : la qualité de votre code. Quels principes de base ont guidés le design de votre code? -R: Nous nous sommes principalement concentrés sur des noms de variables représentatifs, sur une indentation constante, mais surtout sur la structure du projet et sa maintenabilité. -Les *SOLID principles*, l'architecture en couche, ainsi que les principes provenant de [*Clean Code*](https://www.oreilly.com/library/view/clean-code/9780136083238/) ont été nos règles de base. -Cependant, le manque de standards dans les structures de projets de *data science* nous rendait la tâche plus difficile, mais nous avons suivi notre intuition de ce côté-là. +R: Nous nous sommes principalement concentrés sur des noms de variables représentatifs, sur une indentation constante, mais surtout sur la structure du projet et sa maintenabilité. +Les _SOLID principles_, l'architecture en couche, ainsi que les principes provenant de [_Clean Code_](https://www.oreilly.com/library/view/clean-code/9780136083238/) ont été nos règles de base. +Cependant, le manque de standards dans les structures de projets de _data science_ nous rendait la tâche plus difficile, mais nous avons suivi notre intuition de ce côté-là. #### Q: Pouvez-vous nous partager les meilleurs extraits de votre solution? -R: Voici de quelle façon nous avons bâtit la structure générale de notre projet: +R: Voici de quelle façon nous avons bâtit la structure générale de notre projet: ![Structure générale de notre projet.](structure-projet.jpg) @@ -49,21 +50,21 @@ De cette manière, nous croyons avoir bien séparé les différentes composantes #### Q: Mettons de côté la qualité du code. De quelle manière avez-vous attaqué la problématique à résoudre? -R: On a commencé par faire une analyse des données brutes en déterminant les *features* les plus importantes. Ensuite, nous avons séparé les données en jeux de données d'entraînement et de tests avec une proportion 80/20. Nous avons d'abord tenté le coup avec une régression logistique. Cela nous a donné un rappel d’un peu plus que 65% pour les toits verts, ce qui n'était pas optimal à nous yeux. Ainsi, nous avons tenté d’améliorer nos données. Nous avions remarqué un débalancement des classes à prédire dans le jeu de données, alors nous avons fait une augmentation des données en faisant rotationner les images: +R: On a commencé par faire une analyse des données brutes en déterminant les _features_ les plus importantes. Ensuite, nous avons séparé les données en jeux de données d'entraînement et de tests avec une proportion 80/20. Nous avons d'abord tenté le coup avec une régression logistique. Cela nous a donné un rappel d’un peu plus que 65% pour les toits verts, ce qui n'était pas optimal à nous yeux. Ainsi, nous avons tenté d’améliorer nos données. Nous avions remarqué un débalancement des classes à prédire dans le jeu de données, alors nous avons fait une augmentation des données en faisant rotationner les images: ![Exemple d'augmentation des donnnées.](augmentation-donnees.png) -Avec ces données augmentées, la proportion de toits verts était d'environ 50%, au lieu du 20% initial. Nous avons par la suite recréé les *features* pour ce nouveau jeu de données avec le modèle *ResNet50*. Ce pré-traitement sur les données nous a permis d’atteindre un rappel de près de 80% pour les toits verts, ce qui était plus satisfaisant pour nous en termes de performances. +Avec ces données augmentées, la proportion de toits verts était d'environ 50%, au lieu du 20% initial. Nous avons par la suite recréé les _features_ pour ce nouveau jeu de données avec le modèle _ResNet50_. Ce pré-traitement sur les données nous a permis d’atteindre un rappel de près de 80% pour les toits verts, ce qui était plus satisfaisant pour nous en termes de performances. #### Q: Que conseillez-vous aux gens qui veulent commencer à participer à des hackathons? -R: Nous leur conseillons surtout de ne pas avoir peur de "perdre". L'expérience en soi est une victoire. De plus, il est important de bien lire l'énoncé et de bien allouer son temps pour finaliser son projet. C'est bien de passer du temps sur son algorithme pour avoir de meilleures performances, mais au final avoir un produit reproductible et bien construit a beaucoup plus de valeur à nos yeux. +R: Nous leur conseillons surtout de ne pas avoir peur de "perdre". L'expérience en soi est une victoire. De plus, il est important de bien lire l'énoncé et de bien allouer son temps pour finaliser son projet. C'est bien de passer du temps sur son algorithme pour avoir de meilleures performances, mais au final avoir un produit reproductible et bien construit a beaucoup plus de valeur à nos yeux. -#### Q: Quel est le background des membres de l'équipe? Croyez-vous que ce background vous favorisait dans la catégorie *Linus Torvalds*? +#### Q: Quel est le background des membres de l'équipe? Croyez-vous que ce background vous favorisait dans la catégorie _Linus Torvalds_? -R: Deux d'entres nous avons un parcours scolaire relié à l'informatique alors que le 3ème membre à étudié en administration des affaires. Nous ne pensons pas que nos backgrounds nous favorisaient en termes de *Machine Learning*, puisque le domaine est en quelque sorte un mélange d'informatique et de mathématiques. Toutefois, notre expérience en programmation nous a bien servis pour la catégorie de prix que nous avons gagnée. +R: Deux d'entres nous avons un parcours scolaire relié à l'informatique alors que le 3ème membre à étudié en administration des affaires. Nous ne pensons pas que nos backgrounds nous favorisaient en termes de _Machine Learning_, puisque le domaine est en quelque sorte un mélange d'informatique et de mathématiques. Toutefois, notre expérience en programmation nous a bien servis pour la catégorie de prix que nous avons gagnée. -#### Q: Aviez-vous ciblé la catégorie de prix *Linus Torvalds* volontairement durant la compétition? +#### Q: Aviez-vous ciblé la catégorie de prix _Linus Torvalds_ volontairement durant la compétition? R: Oui, sans aucun doute. Sachant que les critères d'évaluation n'incluaient pas explicitement la performance de l'algorithme, nous avions en tête de remettre la meilleure qualité du code possible. Ce n'est pas seulement pour le prix, nous considérons que c'est un point très important dans n'importe quel projet de programmation. Nous savions également que nous avions de l'expérience pour nous démarquer de ce côté. @@ -71,20 +72,19 @@ R: Oui, sans aucun doute. Sachant que les critères d'évaluation n'incluaient p R: Nous avons divisé nos forces en trois, travaillant sur la même tâche pendant 6h! Marc-Alexandre s'occupait de l'approche utilisant des images pour les classifier. De mon côté (Alex), j'utilisais les données structurées pour classifier les images. Taha nous assistait dans nos démarches, dans la transformation des données, ainsi que dans la présentation. Ceci a permis une utilisation optimale de notre temps. - #### Q: Quels ouvrages conseillez-vous aux gens qui veulent améliorer leurs présentations? Et leur performance dans la résolution de ce genre de problématiques? -R: Comme ouvrage, nous recommandons définitivement [*Clean Code*](https://www.oreilly.com/library/view/clean-code/9780136083238/) et [Clean Architecture](http://blog.cleancoder.com/uncle-bob/2012/08/13/the-clean-architecture.html) d'Uncle Bob, ainsi que [GOOS](http://www.growing-object-oriented-software.com/) (Growing Object-Oriented Software Guided by Tests). +R: Comme ouvrage, nous recommandons définitivement [_Clean Code_](https://www.oreilly.com/library/view/clean-code/9780136083238/) et [Clean Architecture](http://blog.cleancoder.com/uncle-bob/2012/08/13/the-clean-architecture.html) d'Uncle Bob, ainsi que [GOOS](http://www.growing-object-oriented-software.com/) (Growing Object-Oriented Software Guided by Tests). Nous recommandons également aux étudiants de l'Université Laval de suivre le cours [GLO-4002](https://www.ulaval.ca/les-etudes/cours/repertoire/detailsCours/glo-4002-qualite-et-metriques-du-logiciel.html) (Qualité et métriques du logiciel). C'est l'un des cours les plus formateurs (offerts à ULaval) pour avoir des bases solides en programmation. -Pour améliorer les performances dans la résolution de problèmes de *Machine Learning*, nous recommandons [Data mining : concepts and techniques](http://myweb.sabanciuniv.edu/rdehkharghani/files/2016/02/The-Morgan-Kaufmann-Series-in-Data-Management-Systems-Jiawei-Han-Micheline-Kamber-Jian-Pei-Data-Mining.-Concepts-and-Techniques-3rd-Edition-Morgan-Kaufmann-2011.pdf) de Jiawei Han, [The Elements of Statistical Learning](https://web.stanford.edu/~hastie/Papers/ESLII.pdf) de Hastie et [Hands-On Machine Learning with Scikit-Learn and TensorFlow](http://index-of.es/Varios-2/Hands%20on%20Machine%20Learning%20with%20Scikit%20Learn%20and%20Tensorflow.pdf) d'Aurélien Géron. +Pour améliorer les performances dans la résolution de problèmes de _Machine Learning_, nous recommandons [Data mining : concepts and techniques](http://myweb.sabanciuniv.edu/rdehkharghani/files/2016/02/The-Morgan-Kaufmann-Series-in-Data-Management-Systems-Jiawei-Han-Micheline-Kamber-Jian-Pei-Data-Mining.-Concepts-and-Techniques-3rd-Edition-Morgan-Kaufmann-2011.pdf) de Jiawei Han, [The Elements of Statistical Learning](https://web.stanford.edu/~hastie/Papers/ESLII.pdf) de Hastie et [Hands-On Machine Learning with Scikit-Learn and TensorFlow](http://index-of.es/Varios-2/Hands%20on%20Machine%20Learning%20with%20Scikit%20Learn%20and%20Tensorflow.pdf) d'Aurélien Géron. #### Q: Est-ce que le hackathon a changé vos plans (études, participations à d'autres événements, cours en ligne, etc.) pour les prochains mois? Quelles sont les prochaines étapes pour vous? Voici leur réponse: -**M-A**: De mon côté, j'ai commencé à en apprendre plus sur le *Machine Learning* l'été passé. Pour le moment, je travaille dans une entreprise où on essaye de voir ce qu'il est possible de faire en recherche appliquée de ce côté et comment on peut tirer notre épingle du jeu dans ce champ d'expertise. L'événement me permet de continuer dans cette veine. +**M-A**: De mon côté, j'ai commencé à en apprendre plus sur le _Machine Learning_ l'été passé. Pour le moment, je travaille dans une entreprise où on essaye de voir ce qu'il est possible de faire en recherche appliquée de ce côté et comment on peut tirer notre épingle du jeu dans ce champ d'expertise. L'événement me permet de continuer dans cette veine. **A**: Pour ma part, le hackathon m'a donné le goût de participer à d'autres événements similaires. Initialement j'étais vraiment stressé de ne pas bien performer, mais en voyant ça plus comme une situation d'apprentissage qu'une compétition, ça devenait beaucoup moins stressant. diff --git a/content/blog/2018-11-17-hackathon-winners-creativity/index.fr.md b/content/blog/2018-11-17-hackathon-winners-creativity/index.fr.md index 7ff2f0340..af2152b83 100644 --- a/content/blog/2018-11-17-hackathon-winners-creativity/index.fr.md +++ b/content/blog/2018-11-17-hackathon-winners-creativity/index.fr.md @@ -4,16 +4,17 @@ author: Laurent Caron et Marc-André Bernier date: '2018-12-08' slug: hackathon-winners-creativity type: post -categories: ["Hackathon"] -tags: ["Interview", "Meetup", "Hackathon"] -description: "Meetup ML Québec | Prix Leonardo da Vinci" -featured: "hackathon-creativity-cover.JPG" -featuredpath: "img/headers/" +categories: ['Hackathon'] +tags: ['Interview', 'Meetup', 'Hackathon'] +description: 'Meetup ML Québec | Prix Leonardo da Vinci' +featured: 'hackathon-creativity-cover.JPG' +featuredpath: 'img/headers/' +aliases: [/blog/2018-11-17-hackathon-winners-creativity/hackathon-winners-creativity/] --- Le 10 novembre 2018 avait lieu à Québec la [Journée hackathon en assurance](https://www.facebook.com/events/185652975580020/), organisée par [MeetupMLQuebec](https://www.facebook.com/MeetupMLQuebec) et présentée en collaboration avec Intact Assurances et Co-operators. -Félicitations à *Les Beans*, gagnants du prix Leonardo da Vinci, remis à l'équipe qui a proposé la solution la plus créative. Les critères étaient à la discrétion des juges, mais ils nous ont partagé leur processus de décision ci-bas. Pour plus de détails sur la problématique ou l'énoncé, rendez-vous sur le [dépôt officiel](https://github.com/dot-layer/meetup-ML-assurance-hackathon) de la compétition. +Félicitations à _Les Beans_, gagnants du prix Leonardo da Vinci, remis à l'équipe qui a proposé la solution la plus créative. Les critères étaient à la discrétion des juges, mais ils nous ont partagé leur processus de décision ci-bas. Pour plus de détails sur la problématique ou l'énoncé, rendez-vous sur le [dépôt officiel](https://github.com/dot-layer/meetup-ML-assurance-hackathon) de la compétition. ![Les juges et les membres de l'équipe *Les Beans*.](MeetupMLQuebec2018_054.JPG) @@ -30,28 +31,26 @@ Au final, le filtre proposé par le photographe était le plus efficace. Nous cr **PBL**: Étant donné le nombre restreint de données, on s'est dit que si on simplifiait les images, on pourrait se permettre d'avoir des modèles plus simples pour faire le classement. - #### Q: Selon vous, pensez-vous que c'est aussi l'élément déterminant qui a fait pencher la balance chez les juges? Définitivement! #### Nous avons posé la même question aux juges de cette catégorie. Rappelons-nous que les critères d'évaluation pour cette catégorie étaient à leur discrétion. Voici leur réponse: -L'inclusion de techniques de photographie à la solution était effectivement l'élément clé dans notre décision. Les organisateurs avaient fourni des pistes de solution, mais *Les Beans* ont osé emprunter un chemin différent. Ça a vraiment démontré que l'équipe était capable de *think outside the box*. Ça démontrait aussi une curiosité au sujet de la problématique à résoudre. +L'inclusion de techniques de photographie à la solution était effectivement l'élément clé dans notre décision. Les organisateurs avaient fourni des pistes de solution, mais _Les Beans_ ont osé emprunter un chemin différent. Ça a vraiment démontré que l'équipe était capable de _think outside the box_. Ça démontrait aussi une curiosité au sujet de la problématique à résoudre. #### Q: Pouvez-vous nous partager les meilleurs extraits de votre solution? Voici quelques exemples qui illustrent l'effet des filtres sur les images. -| Avant | Après | -:---------------:|:-----------------: -![](image-2.png) | ![](image-2.jpg) -![](image-4.png) | ![](image-4.jpg) -![](image-7.png) | ![](image-7.jpg) -![](image-13.png) | ![](image-13.jpg) - -**PBL** : On a voulu simplifier les images pour rendre la détection de toit vert plus facile à l'oeil. On s'est dit que de rajouter une étape de prétraitement de données, qui était basée sur l'avis d'un expert en photographie, pourrait aider le modèle à mieux performer. Par contre, comme le jeu de données devait toujours passer au travers de toutes les couches cachées du réseau de neurones *ResNet50*, cela ne simplifiait pas le modèle pour autant. Pour avoir de meilleurs résultats, avec le peu d'images disponibles, il aurait sûrement fallu qu'on joue avec les couches de *ResNet50* pour l'adapter à nos images simplifiées. De cette façon, nous aurions pu avoir un modèle relativement plus simple et mieux adapté pour nos images plus simples. +| Avant | Après | +| :---------------: | :---------------: | +| ![](image-2.png) | ![](image-2.jpg) | +| ![](image-4.png) | ![](image-4.jpg) | +| ![](image-7.png) | ![](image-7.jpg) | +| ![](image-13.png) | ![](image-13.jpg) | +**PBL** : On a voulu simplifier les images pour rendre la détection de toit vert plus facile à l'oeil. On s'est dit que de rajouter une étape de prétraitement de données, qui était basée sur l'avis d'un expert en photographie, pourrait aider le modèle à mieux performer. Par contre, comme le jeu de données devait toujours passer au travers de toutes les couches cachées du réseau de neurones _ResNet50_, cela ne simplifiait pas le modèle pour autant. Pour avoir de meilleurs résultats, avec le peu d'images disponibles, il aurait sûrement fallu qu'on joue avec les couches de _ResNet50_ pour l'adapter à nos images simplifiées. De cette façon, nous aurions pu avoir un modèle relativement plus simple et mieux adapté pour nos images plus simples. #### Q: Que conseillez-vous aux gens qui veulent commencer à participer à des hackathons? @@ -61,27 +60,23 @@ Voici quelques exemples qui illustrent l'effet des filtres sur les images. **PBL**: Combattre le syndrome de l'imposteur. Je crois que tout le monde peut y trouver son compte si la motivation et le désir d'apprendre y sont. - #### Q: Que conseillez-vous aux gens qui veulent stimuler leur créativité? -**JT**: Je crois que chaque personne est créative à sa manière, mais ce qui peut stimuler la créativité est de s'ouvrir à plusieurs domaines. Par exemple, ce qui est intéressant dans le domaine de l'apprentissage automatique, c'est que ça réunit des personnes de différents domaines allant de la psychologie à la biologie, jusqu'à l'informatique. Ça en fait donc un domaine très créatif et intéressant. +**JT**: Je crois que chaque personne est créative à sa manière, mais ce qui peut stimuler la créativité est de s'ouvrir à plusieurs domaines. Par exemple, ce qui est intéressant dans le domaine de l'apprentissage automatique, c'est que ça réunit des personnes de différents domaines allant de la psychologie à la biologie, jusqu'à l'informatique. Ça en fait donc un domaine très créatif et intéressant. Avoir en main plusieurs sources d'idées peut venir générer la créativité. Comme dans le cas de notre solution: penser au photographe était aucunement mathématique, seulement artistique. - #### Q: Quels ouvrages conseillez-vous aux gens qui veulent améliorer leurs performances dans la résolution de ce genre de problématiques? -**SP**: En tant que grand fan du language R, je recommande [R for Data Science](https://r4ds.had.co.nz/) (qui est d'ailleurs disponible gratuitement sur le web) pour tous ceux et celles qui souhaitent améliorer leurs compétences en analyses de données. L'accent est principalement mis sur le nettoyage et la visualisation des données. +**SP**: En tant que grand fan du language R, je recommande [R for Data Science](https://r4ds.had.co.nz/) (qui est d'ailleurs disponible gratuitement sur le web) pour tous ceux et celles qui souhaitent améliorer leurs compétences en analyses de données. L'accent est principalement mis sur le nettoyage et la visualisation des données. En ce qui concerne les méthodes statistiques, ma bible est [The Elements of Statistical Learning](https://web.stanford.edu/~hastie/Papers/ESLII.pdf), qui possède aussi un équivalent un peu plus facile à lire pour les plus débutants: [An Introduction to Statistical Learning](https://www.ime.unicamp.br/~dias/Intoduction%20to%20Statistical%20Learning.pdf). Ces deux références sont également disponibles gratuitement en ligne. - #### Q: Quel est le background des membres de l'équipe? Croyez-vous que ce background vous favorisait dans la catégorie Leonardo da Vinci? -**JT**: Domaine de l'actuariat, consultation internationale. Je crois que la créativité vient plus de mes intérêts personnels et de mon ouverture d'esprit. +**JT**: Domaine de l'actuariat, consultation internationale. Je crois que la créativité vient plus de mes intérêts personnels et de mon ouverture d'esprit. **SP**: Les trois avons étudié l'actuariat à un certain moment. Maintenant, on se concentre plus particulièrement en statistique et apprentissage machine, que ce soit dans d'autres facultés ou de manière autodidacte. Par rapport aux autres, ça ne nous a probablement pas aidé plus qu'il le faut en ce qui concerne la créativité. - #### Q: Aviez-vous ciblé la catégorie de prix Leonardo da Vinci volontairement avant ou pendant la compétition en faisant des compromis sur les autres prix (présentation, code)? **JT**: Pour être honnête, on visait directement cette catégorie en commençant. Lorsque j'ai rencontré Sam et Phil, ils avaient déjà l'objectif d'obtenir la solution la plus créative bien avant l'événement. Je crois que Phil était très motivé par le prix : des billets pour José Gonzalez. @@ -90,26 +85,23 @@ En ce qui concerne les méthodes statistiques, ma bible est [The Elements of Sta **PBL**: Effectivement, je l'ai déjà vu en spectacle! Aussi, on sait qu'on aurait pu passer la journée à essayer d'optimiser les hyperparamètres pour obtenir les meilleures _metrics_, mais cette approche nous tentait un peu moins. On préférait essayer des nouvelles idées et voir ce qui fonctionne bien. - #### Q: De quelle façon votre équipe a-t-elle alloué les 6 heures à sa disposition? Avec du recul, les auriez-vous allouées de façon différente? **SP**: Après environ une heure, on avait déjà du code qui faisait l'essentiel du travail : un modèle linéaire généralisé, un réseau de neurones et un ensemble d'arbres de décisions. Merci au librairies R keras, glmnet et xgboost! On avait encore de petits bugs avec nos données par contre. -Avec le recul, nous avons passé trop de temps à faire de la recherche en grille (*grid search*) pour trouver de meilleurs hyperparamètres. En rétrospective, j'aurais aimé passez plus de temps pour inclure dans la présentation certains aspects subtiles de nos modèles. On a aussi commencé à préparer notre présentation trop +Avec le recul, nous avons passé trop de temps à faire de la recherche en grille (_grid search_) pour trouver de meilleurs hyperparamètres. En rétrospective, j'aurais aimé passez plus de temps pour inclure dans la présentation certains aspects subtiles de nos modèles. On a aussi commencé à préparer notre présentation trop tard dans la journée. -Par contre, je crois qu'on a bien fait de s'activer pour appliquer des filtres aux photos. Phil a fait ça avec Photoshop directement et a ensuite passé les résultats dans ResNet50 en python. On voulait tout faire ça en python, et John avait même codé un algorithme qui le faisait. Cependant, les résultats en +Par contre, je crois qu'on a bien fait de s'activer pour appliquer des filtres aux photos. Phil a fait ça avec Photoshop directement et a ensuite passé les résultats dans ResNet50 en python. On voulait tout faire ça en python, et John avait même codé un algorithme qui le faisait. Cependant, les résultats en Photoshop semblaient meilleurs. Encore une fois, ça aurait été intéressant d'avoir le code de John dans notre présentation pour montrer ce qu'était notre but ultime. - #### Q: Est-ce que le hackathon a changé vos plans (études, participations à d'autres événements, cours en ligne, etc.) pour les prochains mois? Quelles sont les prochaines étapes pour vous? -**JT**: Oui à 100%, je compte continuer à vouloir en apprendre plus sur le *Machine Learning* et continuer à lire et coder sur ce sujet. +**JT**: Oui à 100%, je compte continuer à vouloir en apprendre plus sur le _Machine Learning_ et continuer à lire et coder sur ce sujet. **SP**: Dans mon cas, ces événements me rappellent toujours que je devrais améliorer mes compétences python! -**PBL**: Ça n'a pas changé mes plans, mais ça confirme que je vais dans la bonne direction. - +**PBL**: Ça n'a pas changé mes plans, mais ça confirme que je vais dans la bonne direction. #### Q: Avez-vous autre chose à partager au monde entier après cette victoire? @@ -117,7 +109,7 @@ Photoshop semblaient meilleurs. Encore une fois, ça aurait été intéressant d **SP**: .layer 4 life. Love. -**PBL**: *Spread love* et allez porter vos restants de nourriture des événements coopératifs dans des endroits qui les redistribuent aux plus démunis. +**PBL**: _Spread love_ et allez porter vos restants de nourriture des événements coopératifs dans des endroits qui les redistribuent aux plus démunis. Merci pour votre participation, encore une fois félicitations, et rendez-vous au prochain événement de [MeetupMLQuebec](https://www.facebook.com/MeetupMLQuebec)! diff --git a/content/blog/2018-11-17-hackathon-winners-presentation/index.fr.md b/content/blog/2018-11-17-hackathon-winners-presentation/index.fr.md index e00f4d13c..6dd1a2b77 100644 --- a/content/blog/2018-11-17-hackathon-winners-presentation/index.fr.md +++ b/content/blog/2018-11-17-hackathon-winners-presentation/index.fr.md @@ -1,19 +1,20 @@ --- title: Entrevue avec les gagnants -author: "Laurent Caron et Marc-André Bernier" +author: 'Laurent Caron et Marc-André Bernier' date: '2018-11-28' slug: hackathon-winners-presentation type: post -categories: ["Hackathon"] -tags: ["Interview", "Meetup", "Hackathon"] -description: "Meetup ML Québec | Prix Martin Luther King Jr." -featured: "hackathon-presentation-cover.png" -featuredpath: "img/headers/" +categories: ['Hackathon'] +tags: ['Interview', 'Meetup', 'Hackathon'] +description: 'Meetup ML Québec | Prix Martin Luther King Jr.' +featured: 'hackathon-presentation-cover.png' +featuredpath: 'img/headers/' +aliases: [/blog/2018-11-17-hackathon-winners-presentation/hackathon-winners-presentation/] --- Le 10 novembre 2018 avait lieu à Québec la [Journée hackathon en assurance](https://www.facebook.com/events/185652975580020/), organisée par [MeetupMLQuebec](https://www.facebook.com/MeetupMLQuebec) et présentée en collaboration par Intact Assurances et Co-operators. -Félicitations à *La revanche du perceptron*, gagnants du prix Martin Luther King Jr., remis à l'équipe qui a réalisé la meilleure présentation. Le tout, en respectant la limite de temps de 2 minutes. Les juges avaient notamment à l'oeil des critères tels que la clarté des propos et l'efficacité à présenter la méthodologie et les résultats. Pour plus de détails sur la problématique ou l'énoncé, rendez-vous sur le [dépôt officiel](https://github.com/dot-layer/meetup-ML-assurance-hackathon) de la compétition. +Félicitations à _La revanche du perceptron_, gagnants du prix Martin Luther King Jr., remis à l'équipe qui a réalisé la meilleure présentation. Le tout, en respectant la limite de temps de 2 minutes. Les juges avaient notamment à l'oeil des critères tels que la clarté des propos et l'efficacité à présenter la méthodologie et les résultats. Pour plus de détails sur la problématique ou l'énoncé, rendez-vous sur le [dépôt officiel](https://github.com/dot-layer/meetup-ML-assurance-hackathon) de la compétition. ![Juges et La revanche du perceptron](MeetupMLQuebec2018_053.jpg) @@ -25,38 +26,38 @@ R: Tout d'abord, c'était notre premier hackathon en tant qu'équipe, donc nous #### Q: Discutons maintenant de ce qui vous a fait gagner le prix : votre présentation. Le temps limite était de 2 minutes, donc vous deviez être efficaces. Quel est le moment clé où vous pensez que vous avez réussi à convaincre les juges? -R: Nous croyons que la démonstration de la méthode utilisée est ce qui a charmé les juges. Nous avons utilisé une technique très semblable à ce qui est utilisé actuellement dans l'industrie et nous avons réussi à résumer clairement et efficacement cette méthode. Nous étions les seuls à entraîner les couches convolutives du réseau ResNet50 sur le jeu de données disponible pour résoudre la problématique. Contrairement aux autres équipes qui ont utilisé directement les *features* fournies pour les analyser avec un algorithme de classification classique, nous avons plutôt ré-entraîné le réseau ResNet50 pour extraire les nôtres. Nous avons mis l'accent sur ce point et avons construit la présentation autour de celui-ci. +R: Nous croyons que la démonstration de la méthode utilisée est ce qui a charmé les juges. Nous avons utilisé une technique très semblable à ce qui est utilisé actuellement dans l'industrie et nous avons réussi à résumer clairement et efficacement cette méthode. Nous étions les seuls à entraîner les couches convolutives du réseau ResNet50 sur le jeu de données disponible pour résoudre la problématique. Contrairement aux autres équipes qui ont utilisé directement les _features_ fournies pour les analyser avec un algorithme de classification classique, nous avons plutôt ré-entraîné le réseau ResNet50 pour extraire les nôtres. Nous avons mis l'accent sur ce point et avons construit la présentation autour de celui-ci. #### Q: Pouvez-vous nous partager cette technique avec des extraits de votre présentation? R: En résumé, nous sommes partis d'un modèle existant pour bénéficier des poids appris par ce dernier. Nous l'avons ensuite modifié pour qu'il soit mieux adapté à la problématique à résoudre. Puis, nous l'avons ré-entraîné à l'aide des données de toits fournies. -Pour les plus curieux, voici les détails de notre solution : +Pour les plus curieux, voici les détails de notre solution : -ResNet50 est un réseau pré-entraîné dans le cadre de la compétition [ImageNet](https://www.quora.com/What-is-the-ImageNet-competition) sur des millions d'images. Cependant, l'objectif de ce réseau est de discriminer 1000 classes d'objets. Dans notre cas, nous n'en avions que 2 à discriminer : toit vert, ou non. Nous avons donc remplacé la couche de sortie originale par une neurone de sortie sigmoïde, donnant une interprétation probabiliste de notre résultat. De cette façon, on peut prédire avec la couche de sortie du réseau la probabilité que l'image a d’appartenir à chacune des 2 classes, selon les informations qu'a réussi à collecter le réseau de neurones en entraînement. Nous avons également ajouté quelques couches cachées avant la couche de sortie. +ResNet50 est un réseau pré-entraîné dans le cadre de la compétition [ImageNet](https://www.quora.com/What-is-the-ImageNet-competition) sur des millions d'images. Cependant, l'objectif de ce réseau est de discriminer 1000 classes d'objets. Dans notre cas, nous n'en avions que 2 à discriminer : toit vert, ou non. Nous avons donc remplacé la couche de sortie originale par une neurone de sortie sigmoïde, donnant une interprétation probabiliste de notre résultat. De cette façon, on peut prédire avec la couche de sortie du réseau la probabilité que l'image a d’appartenir à chacune des 2 classes, selon les informations qu'a réussi à collecter le réseau de neurones en entraînement. Nous avons également ajouté quelques couches cachées avant la couche de sortie. -Pour ce qui est de l'entraînement, notre système permettait -d'entraîner d'abord les couches cachées ajoutées. Puis, un autre entraînement, incluant trois des couches de convolution du réseau ResNet, était effectué. Ce dernier permettait de personnaliser les *features* -extraites par le réseau en fonction des résultats obtenus en sortie. En effet, le réseau ResNet50 n'a pas été entraîné spécifiquement pour extraire des *features* relatives à des images de toits, étant donné que les images et classes de ImageNet sont bien plus variées. C'est ainsi que nous nous distinguions principalement, soit en obtenant des *features* personnalisées au problème permettant donc une décision plus optimale. Tout ça se faisait en un temps +Pour ce qui est de l'entraînement, notre système permettait +d'entraîner d'abord les couches cachées ajoutées. Puis, un autre entraînement, incluant trois des couches de convolution du réseau ResNet, était effectué. Ce dernier permettait de personnaliser les _features_ +extraites par le réseau en fonction des résultats obtenus en sortie. En effet, le réseau ResNet50 n'a pas été entraîné spécifiquement pour extraire des _features_ relatives à des images de toits, étant donné que les images et classes de ImageNet sont bien plus variées. C'est ainsi que nous nous distinguions principalement, soit en obtenant des _features_ personnalisées au problème permettant donc une décision plus optimale. Tout ça se faisait en un temps d'entraînement relativement court, car on ne réentraînait que 3 couches de ResNet. Finalement, nous avons aussi divisé notre jeu de données en un jeu d'entraînement et un jeu de validation. Cela nous permettait d'obtenir un jeu pour entraîner notre réseau et un autre pour obtenir une idée de nos performances en généralisation (avec des données pour lesquelles le réseau n'était pas entraîné). Nous avons cependant manqué de temps et n'avons eu le temps de tester notre réseau qu'avec une neurone sigmoïde en sortie et 5 époques d'entraînement. #### Q: Votre technique s'est évidemment distinguée de celles des autres équipes. Qu'en est-il de votre méthode de travail? -R: Nous avons utilisé une méthode de programmation que nous appelons *co-programming*. Un programme, pendant que l'autre fournit des conseils et cherche dans la documentation des librairies. Nous interchangions fréquemment ces rôles. Pour plus de détails, vous pouvez consulter la [page Wikipédia](https://fr.wikipedia.org/wiki/Programmation_en_bin%C3%B4me) dédiée à ce sujet. +R: Nous avons utilisé une méthode de programmation que nous appelons _co-programming_. Un programme, pendant que l'autre fournit des conseils et cherche dans la documentation des librairies. Nous interchangions fréquemment ces rôles. Pour plus de détails, vous pouvez consulter la [page Wikipédia](https://fr.wikipedia.org/wiki/Programmation_en_bin%C3%B4me) dédiée à ce sujet. -#### Q: Quel est votre *background* ? Croyez-vous que cela vous favorisait dans la catégorie Martin Luther King Jr.? +#### Q: Quel est votre _background_ ? Croyez-vous que cela vous favorisait dans la catégorie Martin Luther King Jr.? -R: Nous sommes tous les deux étudiants au baccalauréat en génie électrique. Nous ne pensons pas que notre *background* nous favorisait particulièrement dans cette catégorie, car nous n'avons pas souvent à faire des présentations devant un auditoire. +R: Nous sommes tous les deux étudiants au baccalauréat en génie électrique. Nous ne pensons pas que notre _background_ nous favorisait particulièrement dans cette catégorie, car nous n'avons pas souvent à faire des présentations devant un auditoire. #### Q: Aviez-vous ciblé la catégorie de prix Martin Luther King Jr. avant ou pendant la compétition en faisant des compromis sur les autres prix (créativité, code) ? -R: Non. Pour être bien honnête, nous avons été très surpris par notre victoire, car le niveau des autres équipes était très élevé. +R: Non. Pour être bien honnête, nous avons été très surpris par notre victoire, car le niveau des autres équipes était très élevé. #### Q: De quelle façon votre équipe a-t-elle alloué les 6 heures à sa disposition? Avec du recul, les auriez-vous allouées de façon différente? -R: Nous avons commencé par choisir la méthode à implémenter pour résoudre le problème ainsi que la librairie *Python* à utiliser pendant environ une heure. Ensuite, nous avons passé environ deux heures pour nous familiariser avec la librairie Keras, car nous n'avions pas d'expérience avec l'apprentissage profond en *Python*. Le reste de la journée a été utilisé pour implémenter la solution. En parallèle, Étienne a commencé la présentation lors de la dernière heure. Nous croyons que notre utilisation du temps était correcte. +R: Nous avons commencé par choisir la méthode à implémenter pour résoudre le problème ainsi que la librairie _Python_ à utiliser pendant environ une heure. Ensuite, nous avons passé environ deux heures pour nous familiariser avec la librairie Keras, car nous n'avions pas d'expérience avec l'apprentissage profond en _Python_. Le reste de la journée a été utilisé pour implémenter la solution. En parallèle, Étienne a commencé la présentation lors de la dernière heure. Nous croyons que notre utilisation du temps était correcte. Si c'était à refaire, nous utiliserions une autre librairie, car nous avons perdu énormément de temps à reformater les données et à essayer de faire fonctionner notre solution. Également, nous aurions peut-être dû tester les librairies à utiliser d'avance, car nous avons eu beaucoup de problèmes avec le formatage des données. @@ -72,8 +73,8 @@ R: Nous conseillons les ouvrages suivants: - L'excellent [Comment se faire des amis et influencer les autres](http://www.editions-homme.com/comment-se-faire-amis-influencer-autres-nouvelle-edition/dale-carnegie/livre/9782764010310) de Dale Carnegie propose des moyens efficaces de construire une présentation claire qui saura convaincre un auditoire. - En ce qui concerne la résolution de problème, [How to Solve It](https://press.princeton.edu/titles/669.html) de George Pólya présente une méthode générale pour résoudre des problèmes de type mathématique. -- Pour le *Machine Learning* en général, [Introduction to Machine Learning](https://mitpress.mit.edu/books/introduction-machine-learning), écrit par Ethem Alpaydin au MIT Press. -- Finalement, [Deep Learning](https://www.deeplearningbook.org/) est la référence en matière de réseau de neurones. +- Pour le _Machine Learning_ en général, [Introduction to Machine Learning](https://mitpress.mit.edu/books/introduction-machine-learning), écrit par Ethem Alpaydin au MIT Press. +- Finalement, [Deep Learning](https://www.deeplearningbook.org/) est la référence en matière de réseau de neurones. #### Q: Est-ce que le hackathon a changé vos plans (études, participations à d'autres événements, cours en ligne, etc.) pour les prochains mois? Quelles sont les prochaines étapes pour vous? diff --git a/content/blog/2018-11-17-stein-baseball/index.en.md b/content/blog/2018-11-17-stein-baseball/index.en.md index 9500c4d24..fca30f160 100644 --- a/content/blog/2018-11-17-stein-baseball/index.en.md +++ b/content/blog/2018-11-17-stein-baseball/index.en.md @@ -4,11 +4,12 @@ slug: stein-baseball author: Samuel Perreault description: "A simple explanation of Stein's paradox through the famous baseball example of Efron and Morris" date: '2019-01-05' -categories: ["Statistics"] +categories: ['Statistics'] type: post -tags: ["Estimation", "Paradox"] -featured: "stein-baseball.jpg" -featuredpath: "img/headers/" +tags: ['Estimation', 'Paradox'] +featured: 'stein-baseball.jpg' +featuredpath: 'img/headers/' +aliases: [/blog/2018-11-17-stein-baseball/stein-baseball/] ---

@@ -25,24 +26,30 @@ What makes the maximum likelihood estimator (MLE) so useful is that it is consis Stein's paradox is attributed to Charles Stein (1920-2016), an American mathematical statistician who spent most of his career at Stanford University. Stein is remembered by his colleagues not only for his exceptional work, but also for his strong belief in basic human rights and passionate social activism. In an [interview by Y.K. Leong](http://www2.ims.nus.edu.sg/imprints/interviews/CharlesStein.pdf), the very first question touched his statistical work (verifying weather broadcasts for understanding how weather might affect wartime activities) for the Air Force during World War II; Stein's first words are unequivocal: > First I should say that I am strongly opposed to war and -to military work. Our participation in World War II was -necessary in the fight against fascism and, in a way, I am -ashamed that I was never close to combat. However, I -have opposed all wars by the United States since then and -cannot imagine any circumstances that would justify war -by the United States at the present time, other than very -limited defensive actions. +> to military work. Our participation in World War II was +> necessary in the fight against fascism and, in a way, I am +> ashamed that I was never close to combat. However, I +> have opposed all wars by the United States since then and +> cannot imagine any circumstances that would justify war +> by the United States at the present time, other than very +> limited defensive actions. According to [Stanford News](https://news.stanford.edu/2016/12/01/charles-m-stein-extraordinary-statistician-anti-war-activist-dies-96/), the man who was called the “Einstein of the Statistics Department”, was also the first Stanford professor arrested for protesting apartheid and was often involved in anti-war protests. -On the math-stat side, Stein is the author of a very influential/controversial paper entitled [*Inadmissibility of the Usual Estimator for the Mean of a Multivariate Normal Distribution* (1956)](https://apps.dtic.mil/dtic/tr/fulltext/u2/1028390.pdf). To understand what it is about, we need to understand what it means for an estimator to be admissible, which is a statement about its performance. For the purpose, let us use Stein's setup where `$X = (X_1,X_2,X_3)$` is such that each component `$X_i$` is independent and normally distributed, that is `$X_i \sim N(\theta_i,1)$`. Assessing the performance of an estimator `$\hat\theta(X) = \hat\theta = (\hat\theta_1,\hat\theta_2,\hat\theta_3)$` usually involves a loss function `$ L(\hat\theta, \theta) $`, whose purpose is to quantify how much `$\hat\theta$` differs from the true value `$\theta$` it estimates. The loss function that interested Stein was the (very popular) squared Euclidean distance +On the math-stat side, Stein is the author of a very influential/controversial paper entitled [_Inadmissibility of the Usual Estimator for the Mean of a Multivariate Normal Distribution_ (1956)](https://apps.dtic.mil/dtic/tr/fulltext/u2/1028390.pdf). To understand what it is about, we need to understand what it means for an estimator to be admissible, which is a statement about its performance. For the purpose, let us use Stein's setup where `$X = (X_1,X_2,X_3)$` is such that each component `$X_i$` is independent and normally distributed, that is `$X_i \sim N(\theta_i,1)$`. Assessing the performance of an estimator `$\hat\theta(X) = \hat\theta = (\hat\theta_1,\hat\theta_2,\hat\theta_3)$` usually involves a loss function `$ L(\hat\theta, \theta) $`, whose purpose is to quantify how much `$\hat\theta$` differs from the true value `$\theta$` it estimates. The loss function that interested Stein was the (very popular) squared Euclidean distance `$$ - L(\hat\theta(X), \theta) = ||\hat\theta - \theta ||^2 = \sum_i (\hat\theta_i - \theta_i)^2. -$$` +L(\hat\theta(X), \theta) = ||\hat\theta - \theta ||^2 = \sum_i (\hat\theta_i - \theta_i)^2. + +$$ +` Assessing the performance of `$\hat\theta$` directly with `$L$` has the disadvantage of depending on the observed data `$X$`. We overcome such limitation by taking the expectation of `$L(\hat\theta(X),\theta)$` over `$X$`, that is we consider -`$$ - R(\hat\theta, \theta) = \mathbb{E}_X\ L(\hat\theta(X), \theta). -$$` +` +$$ + +R(\hat\theta, \theta) = \mathbb{E}\_X\ L(\hat\theta(X), \theta). + +$$ +` This latter is called the mean squared error, and this is what we try to minimize. An estimator `$\hat\theta$` is deemed inadmissible when there exists at least one estimator `$\hat\vartheta$` for which `$R(\hat\theta, \theta) \geq R(\hat\vartheta, \theta)$` for all `$\theta$`, with the equality being strict for at least one value of `$\theta$`. In other words, an estimator is inadmissible if and only if there exists another estimator that dominates it. Stein showed that, for the normal model, the "usual estimator", which is the MLE, not only isn't always the best (in terms of mean squared error), but never is the best! In their 1977 paper [*Stein's Paradox in Statistics*](https://www.researchgate.net/profile/Carl_Morris/publication/247647698_Stein's_Paradox_in_Statistics/links/53da1fe60cf2631430c7f8ed.pdf) (where the term "Stein's paradox" seems to have appeared for the first time), Bradley Efron and Carl Morris loosely described it as follows: @@ -62,17 +69,29 @@ For the rest of the post, I focus on a concrete application of Stein's theory. I Let us go back in time and suppose we are at this exact date in 1970. To predict the batting averages of each player for the remainder of the season, it seems natural (with no other information) to use their current batting averages. Again, this coincides with the MLE under the normal model, and this is why the column labeled `$\hat\mu_i^{(\mathrm{MLE})}$` simply shows the same ratios, this time in decimal form. To get a glimpse of why the MLE is sub-optimal, define a class of estimators parametrized by `$c$` as follows: let `$\hat\mu^{(c)} = (\hat\mu_{1}^{(c)}, \dots, \hat\mu_{18}^{(c)})$` where -`$$ - \hat\mu_{i}^{(c)} = \bar{\mu} + c(\hat\mu_i^{(\mathrm{MLE})} - \bar{\mu}), -$$` +` +$$ + + \hat\mu_{i}^{(c)} = \bar{\mu} + c(\hat\mu_i^{(\mathrm{MLE})} - \bar{\mu}), + +$$ +` and `$\bar{\mu}$` is the average of all means, *i.e.* the grand mean. In our case, -`$$ - \bar{\mu} = \frac{1}{18} \sum_{i=1}^{18} \hat\mu_i^{(\mathrm{MLE})} -$$` +` +$$ + +\bar{\mu} = \frac{1}{18} \sum\_{i=1}^{18} \hat\mu_i^{(\mathrm{MLE})} + +$$ +` is the overall batting average of the `$d=18$` players. Note that `$\hat\mu^{(MLE)} = \hat\mu^{(1)}$`, and so the MLE is indeed part of the class of estimators we consider. Now, define the James-Stein estimator as the one minimizing the mean squared error, that is `$\hat\mu_i^{(JS)} = \hat\mu_{i}^{(c^*)}$` for -`$$ -c^* := {\rm argmin}_{c \in [0,1]}\ R(\hat\mu^{(c)}, \mu), -$$` +` +$$ + +c^\* := {\rm argmin}\_{c \in [0,1]}\ R(\hat\mu^{(c)}, \mu), + +$$ +` where `$\mu = (\mu_1, \dots, \mu_{18})$` represents the *true/ideal* batting averages of the players (may they exist) that we are trying to estimate. As the definition of `$\hat\mu^{(c)}$` suggests, Stein's method consists of shrinking the individual batting average of each player towards their grand average: in Efron's and Morris' words, > if a player's hitting record is better than the grand average, then it must be reduced; if he is not hitting as well as the grand average, then his hitting record must be increased. @@ -80,9 +99,13 @@ where `$\mu = (\mu_1, \dots, \mu_{18})$` represents the *true/ideal* batting ave If the MLE `$\hat\mu_i^{(\mathrm{MLE})}$` were to minimize the mean squared error `$R(\hat\mu^{(c)}, \mu)$`, it would mean `$c^* = 1$`. This is where Stein's result comes into play: it states that if `$c^*$` minimizes the mean squared error, then it must be the case that `$c^*<1$` and so `$\hat\mu^{(\mathrm{JS})} \neq \hat\mu^{(\mathrm{MLE})}$`. The method proposed by [James and Stein (1961)](http://www.stat.yale.edu/~hz68/619/Stein-1961.pdf) to estmimate `$c^*$` leads to the value `$c^*=.212$`, and since in our example the grand average is `$\bar\mu = .265$`, we get -`$$ - \hat\mu_i^{(\mathrm{JS})} = .265 + (.212)(\hat\mu_i^{(\mathrm{MLE})} - .265) -$$` +` +$$ + + \hat\mu_i^{(\mathrm{JS})} = .265 + (.212)(\hat\mu_i^{(\mathrm{MLE})} - .265) + +$$ +` [Efron and Morris](https://www.researchgate.net/profile/Carl_Morris/publication/247647698_Stein's_Paradox_in_Statistics/links/53da1fe60cf2631430c7f8ed.pdf) provide a very intuitive illustration of the effect of shrinkage in this particular example.
@@ -93,9 +116,11 @@ As we can see, everyone is pulled by the grand average of batting averages. Comi It can seem puzzling that, to estimate Clemente's batting average (the highest), using Alvis' batting average (the lowest) should help. According to our formulas, if Alvis' batting average `$\hat\mu_{\mathrm{Alvis}}^{(\mathrm{MLE})}$` was different, then our guess `$\hat\mu_{\mathrm{Clemente}}^{(\mathrm{JS})}$` for Clemente would be different as well (because `$\bar\mu$` would be different). -It becomes more intuitive when you realize that the value of `$c^*$` actually depends on `$n$`, the number of observations available to us (times at bat in our example). As `$n$` increases, the optimal value `$c^*$` gets closer to `$1$`, and so less shrinkage is applied. Stein's Theorem states that `$c^*<1$` no matter what; yet, it might be very close to one, and so `$\hat\mu_{i}^{(\mathrm{MLE})}$` and `$\hat\mu_{i}^{(\mathrm{JS})}$` might be extremely similar. +It becomes more intuitive when you realize that the value of `$c^*$` actually depends on `$n$`, the number of observations available to us (times at bat in our example). As `$n$` increases, the optimal value `$c^*$` gets closer to `$1$`, and so less shrinkage is applied. Stein's Theorem states that `$c^*<1$` no matter what; yet, it might be very close to one, and so `$\hat\mu_{i}^{(\mathrm{MLE})}$` and `$\hat\mu_{i}^{(\mathrm{JS})}$` might be extremely similar. This last property of the James-Stein estimator is similar to that of bayesian estimators, which relies more and more on the data (and less on the prior distribution) as the number of observations increases. Indeed, Efron and Morris identified Stein's method, designed under a strictly frequentist regime, as an empirical Bayes rule (inference procedures where the prior is estimated from the data as well, instead of being set by the user). For more details on Stein's Paradox and its relation to empirical Bayes methods, I recommend the book [**Computer Age Statistical Inference: Algorithms, Evidence, and Data Science**](https://web.stanford.edu/~hastie/CASI_files/PDF/casi.pdf) by Efron and Hastie, which provides, among many otherthings, a nice historical perspective of modern methods used by statisticians and a gentle introduction to the *connections and disagreements* relating and opposing the two main statistical theories: frequentism and bayesianism. I have to give some credit to Laurent Caron, Stéphane Caron and Simon Youde for their useful comments on preliminary versions of the post. + +$$ diff --git a/content/blog/2019-02-01-we-mastered-the-art-of-programming/index.en.md b/content/blog/2019-02-01-we-mastered-the-art-of-programming/index.en.md index 5d7adb22f..c385abc2b 100644 --- a/content/blog/2019-02-01-we-mastered-the-art-of-programming/index.en.md +++ b/content/blog/2019-02-01-we-mastered-the-art-of-programming/index.en.md @@ -4,53 +4,41 @@ author: Jean-Thomas Baillargeon date: '2019-02-01' slug: we-mastered-the-art-of-programming type: post -categories: ["Software engineering"] -tags: ["Software Engineering", "Best Practices"] -description: "" -featured: "we-mastered-the-art-of-programming-cover.jpeg" -featuredpath: "img/headers/" +categories: ['Software engineering'] +tags: ['Software Engineering', 'Best Practices'] +description: '' +featured: 'we-mastered-the-art-of-programming-cover.jpeg' +featuredpath: 'img/headers/' +aliases: [/blog/2019-02-01-we-mastered-the-art-of-programming/we-mastered-the-art-of-programming/] --- - This is probably going to sound cliché and trivial but I just realized that I learned how to use a computer before learning to use a pen. I launched my favourite game on a MS/DOS terminal a few years before writing my name on paper. - -This is cliché and trivial since this holds true for many of us data scientists growing up with a computer. We found a way to download music out of Napster, Limewire or torrents - whatever was working at that time. We figured how to burn our friends’ game on a CD-ROM and then crack it with a fake CD KEY. We learned how to repair the family’s printer. We understood how to connect our laptop to Eduroam. We made very serious and scientific calculations for school points. We then moved to a workplace where we grasp the depth of the `vLookup`. We connected thousands of *Excel* spreadsheets together. We mastered the art of programming. - +This is cliché and trivial since this holds true for many of us data scientists growing up with a computer. We found a way to download music out of Napster, Limewire or torrents - whatever was working at that time. We figured how to burn our friends’ game on a CD-ROM and then crack it with a fake CD KEY. We learned how to repair the family’s printer. We understood how to connect our laptop to Eduroam. We made very serious and scientific calculations for school points. We then moved to a workplace where we grasp the depth of the `vLookup`. We connected thousands of _Excel_ spreadsheets together. We mastered the art of programming. ## problem solving at it's finest. - What connects the dot are the curiosity and the satisfaction of solving a problem, a computer problem. We make things work. This is how we get points on a school assignment. This is how our boss sees us as the achiever. This is how we create pure value. Based on this, it only makes sense that the faster you solve a computer problem, the better you are. +But computer problems seldom get stashed away once solved. They come back to haunt you and require adjustments to the code so it fits the new constraints. Great, a new problem! You have to find the fastest way to hack the code. This is fun until one or two things happen: either the code you have to revise comes from a _bad_ programmer or something breaks every time you alter something else. It will eventually work, but it’ll take a little longer than the previous iteration. This continues, you realize that the _bad_ programmer is your past self a few months ago and you fear modifying anything since you have no faith this [_Jenga_](https://secure.img1-fg.wfcdn.com/im/93997415/resize-h800%5Ecompr-r85/4885/48852016/Jenga%25AE+giant%25u2122+premium+jeu+de+bois+franc.jpg) will hold at all. -But computer problems seldom get stashed away once solved. They come back to haunt you and require adjustments to the code so it fits the new constraints. Great, a new problem! You have to find the fastest way to hack the code. This is fun until one or two things happen: either the code you have to revise comes from a *bad* programmer or something breaks every time you alter something else. It will eventually work, but it’ll take a little longer than the previous iteration. This continues, you realize that the *bad* programmer is your past self a few months ago and you fear modifying anything since you have no faith this [*Jenga*](https://secure.img1-fg.wfcdn.com/im/93997415/resize-h800%5Ecompr-r85/4885/48852016/Jenga%25AE+giant%25u2122+premium+jeu+de+bois+franc.jpg) will hold at all. - - -Maybe if you took a little more time to think of how future you would feel using it, things would have been different. Maybe you would have cleaned the code. Maybe you would have explained the [magic number](https://en.wikipedia.org/wiki/Magic_number_(programming)) (why divide by 8.2?). Maybe side effects would have been commented. Well, this is not important for now, because the code *works*. - - -But this did not apply to me. I could jump back in my code, read the witty comment I wrote and remember what `i`, `j` and `k` represented in the matrix `X`. I make functions, so I [don't repeat myself](https://en.wikipedia.org/wiki/Don%27t_repeat_yourself). I had a bunch of smell tests that let me know if I was breaking something. No really, I mastered the art of programming. +Maybe if you took a little more time to think of how future you would feel using it, things would have been different. Maybe you would have cleaned the code. Maybe you would have explained the [magic number]() (why divide by 8.2?). Maybe side effects would have been commented. Well, this is not important for now, because the code _works_. +But this did not apply to me. I could jump back in my code, read the witty comment I wrote and remember what `i`, `j` and `k` represented in the matrix `X`. I make functions, so I [don't repeat myself](https://en.wikipedia.org/wiki/Don%27t_repeat_yourself). I had a bunch of smell tests that let me know if I was breaking something. No really, I mastered the art of programming. ## Until one day. - I was working at a [Tech Startup](https://www.xpertsea.com/) and my team was working on a shrimp detection model. We were a few guys with different backgrounds. Everyone was a software engineer except for myself; I was an actuary, the most knowledgeable in statistics among us. We were pitted against one another to solve this shrimp problem. Plot twist. I was not the successful one, for reasons I was not even aware of. - It started pretty well. I quickly built a script that let me grab the data from an unstructured source and convert it to a structured format. I would then run diagnoses to find any outliers or collinearity between features. Once the data was cleaned, another script generated a regression model using a stepwise algorithm. The model would generate predictions and save them into a file. The last script was doing graphs and basic analyses. Pretty standard data science duty. The report was done deal and I could quickly deliver great results to the team. - Then we had to evaluate the evolution of the model. This analysis became a weekly affair, then a daily one. Every day, the analysis got more complicated and the dataset got bigger and weirder. We cross-sectioned the data for certain shrimp providers, age, average size, country, quality of water — name it. We changed the image-processing method, we added countless custom features, we used various metrics, we corrected the dataset, etc. This is where I hit the wall. The software engineer guy was pumping his analysis a few hours before me, every day. - ## Why isn't this working ? - This called for my problem solving skills. How can I achieve faster production? By better understanding what he was doing. -First thing I saw is the pre-processing step was nightly done by a robot. The second thing I saw was his [cache](https://en.wikipedia.org/wiki/Cache_(computing)), where all the data were ready for treatment and intermediate values were even pre-calculated (for data grouping and other analysis). The third thing was the modeling part, where everything was so tightly bundled that he could multi-thread all different data cross-sections in one operation. The best part of all this was — all those things were done by 9 am when he arrived. Least to say that gave him plenty of time to work on his analysis. +First thing I saw is the pre-processing step was nightly done by a robot. The second thing I saw was his [cache](), where all the data were ready for treatment and intermediate values were even pre-calculated (for data grouping and other analysis). The third thing was the modeling part, where everything was so tightly bundled that he could multi-thread all different data cross-sections in one operation. The best part of all this was — all those things were done by 9 am when he arrived. Least to say that gave him plenty of time to work on his analysis. I asked him if he could lend me a piece or two to integrate them into my scripts. As a good teammate, he obliged and provided me with a chunk of code. My pride was hurt enough that I went back to my desk alone and worked on it by myself. It was a relatively big chunk of code, so I expected a proportionate effort to plug everything and test. I opened the code and could quickly locate the lines that would require modifications. The music of typing was flowing into my ears — yes I had my [mechanical keyboard](http://www.wasdkeyboards.com/index.php/products/code-keyboard/code-87-key-mechanical-keyboard.html) at that time. It only took me around half an hour to do everything. I made it work without any unexpected side effects the first time I launched the script. @@ -64,7 +52,7 @@ This is the moment the epiphany came. I was not the one who has been doing a gre First of all, there were things my teammate knew that I didn't know that I didn't know. Things like data pipeline, [design patterns](https://sourcemaking.com/design_patterns) and [principles](https://en.wikipedia.org/wiki/SOLID), unit testing, [clean code](https://en.wikipedia.org/wiki/Worship), software architecture, etc. These are the normal distribution for the actuary, the $(X'X)^{-1}$ for the statistician. The ground on which everything is built. Clearly my hacking and school background never prepared me for this. -My teammate had an holistic view of the problem. I was too busy *making things work* while my teammate was busy figuring *the best way* to make it work. This subtle difference is crucial. A good solution will make something work right now. The best solution makes something work right now AND consider the future user. Even if it means taking extra time to step back and build in the right direction. +My teammate had an holistic view of the problem. I was too busy _making things work_ while my teammate was busy figuring _the best way_ to make it work. This subtle difference is crucial. A good solution will make something work right now. The best solution makes something work right now AND consider the future user. Even if it means taking extra time to step back and build in the right direction. Clearly no solution could (or should) have initially planted all the seeds for the craziness that came after the first request. But my stubbornness to produce a quick solution recycling my first approach without reconsidering possible paradigm shift in the problem doomed me in the long run. diff --git a/content/blog/2019-02-01-we-mastered-the-art-of-programming/index.fr.md b/content/blog/2019-02-01-we-mastered-the-art-of-programming/index.fr.md index 959b820b2..66304ab89 100644 --- a/content/blog/2019-02-01-we-mastered-the-art-of-programming/index.fr.md +++ b/content/blog/2019-02-01-we-mastered-the-art-of-programming/index.fr.md @@ -4,40 +4,41 @@ author: Jean-Thomas Baillargeon date: '2019-02-01' slug: on-a-maitrise-lart-de-la-programmation type: post -categories: ["Ingénierie logicielle"] -tags: ["Ingénierie Logicielle", "Bonnes Pratiques"] -description: "" -featured: "we-mastered-the-art-of-programming-cover.jpeg" -featuredpath: "img/headers/" +categories: ['Ingénierie logicielle'] +tags: ['Ingénierie Logicielle', 'Bonnes Pratiques'] +description: '' +featured: 'we-mastered-the-art-of-programming-cover.jpeg' +featuredpath: 'img/headers/' +aliases: [/blog/2019-02-01-we-mastered-the-art-of-programming/on-a-maitrise-lart-de-la-programmation/] --- Ça va probablement paraître cliché et trivial, mais je viens de réaliser que j’ai appris à utiliser un ordinateur avant d’apprendre à utiliser un crayon. J'ai lancé mon jeu préféré dans un terminal MS / DOS quelques années avant d'apprendre à écrire mon nom. -C'est cliché et trivial, parce que c'est vrai pour beaucoup d'entre nous, les *data scientists* qui ont grandi avec un ordinateur. On a trouvé un moyen de télécharger de la musique sur Napster, Limewire ou avec les torrents - peu importe ce qui fonctionnait à l'époque. On gravé les jeux de nos amis sur des CD-ROM, puis trouvé le moyen de les *cracker* avec un faux *CD-Key*. On a appris à réparer l’imprimante de la famille. On a compris comment connecter notre portable à Eduroam. On a fait des calculs très sérieux et scientifiques pour des points à l'école. On est arrivés sur le marché du travail, où on a saisi la profondeur du `vLookup`. On a connecté des milliers de feuilles de calcul *Excel* ensemble. On a maîtrisé l'art de la programmation. +C'est cliché et trivial, parce que c'est vrai pour beaucoup d'entre nous, les _data scientists_ qui ont grandi avec un ordinateur. On a trouvé un moyen de télécharger de la musique sur Napster, Limewire ou avec les torrents - peu importe ce qui fonctionnait à l'époque. On gravé les jeux de nos amis sur des CD-ROM, puis trouvé le moyen de les _cracker_ avec un faux _CD-Key_. On a appris à réparer l’imprimante de la famille. On a compris comment connecter notre portable à Eduroam. On a fait des calculs très sérieux et scientifiques pour des points à l'école. On est arrivés sur le marché du travail, où on a saisi la profondeur du `vLookup`. On a connecté des milliers de feuilles de calcul _Excel_ ensemble. On a maîtrisé l'art de la programmation. ## La résolution de problèmes à son meilleur. -Ce qui attache le tout ensemble est la curiosité et la satisfaction de résoudre un problème, un problème informatique. On fait fonctionner les choses. C'est comme ça qu'on obtient des points pour un travail scolaire. C’est comme ça que notre patron nous voit comme le meilleur. C'est comme ça qu'on crée de la valeur. Basé là-dessus, il est logique que plus on résout rapidement un problème, meilleur on est perçus. +Ce qui attache le tout ensemble est la curiosité et la satisfaction de résoudre un problème, un problème informatique. On fait fonctionner les choses. C'est comme ça qu'on obtient des points pour un travail scolaire. C’est comme ça que notre patron nous voit comme le meilleur. C'est comme ça qu'on crée de la valeur. Basé là-dessus, il est logique que plus on résout rapidement un problème, meilleur on est perçus. -Mais les problèmes informatiques sont rarement tablettés une fois résolus. Ils reviennent vous hanter et nécessitent des ajustements au code pour s'adapter aux nouvelles contraintes. Super, un nouveau problème! Il faut trouver le moyen le plus rapide de *hacker* le code. C'est le fun jusqu'à ce qu'une ou deux choses se produisent: soit le code que vous devez réviser provient d'un *mauvais* programmeur, soit quelque chose se brise chaque fois que vous modifiez quelque chose d'autre. Tout finit par fonctionner, mais ça prendra un peu plus longtemps que la dernière fois. Ça continue, vous réalisez que le *mauvais* programmeur c'est vous il y a quelques mois et que vous avez peur de modifier quoi que ce soit dans le code puisque vous n’avez aucune confiance que ce [*Jenga*](https://secure.img1-fg.wfcdn.com/im/93997415/resize-h800%5Ecompr-r85/4885/48852016/Jenga%25AE+giant%25u2122+premium+jeu+de+bois+franc.jpg) tiendra. +Mais les problèmes informatiques sont rarement tablettés une fois résolus. Ils reviennent vous hanter et nécessitent des ajustements au code pour s'adapter aux nouvelles contraintes. Super, un nouveau problème! Il faut trouver le moyen le plus rapide de _hacker_ le code. C'est le fun jusqu'à ce qu'une ou deux choses se produisent: soit le code que vous devez réviser provient d'un _mauvais_ programmeur, soit quelque chose se brise chaque fois que vous modifiez quelque chose d'autre. Tout finit par fonctionner, mais ça prendra un peu plus longtemps que la dernière fois. Ça continue, vous réalisez que le _mauvais_ programmeur c'est vous il y a quelques mois et que vous avez peur de modifier quoi que ce soit dans le code puisque vous n’avez aucune confiance que ce [_Jenga_](https://secure.img1-fg.wfcdn.com/im/93997415/resize-h800%5Ecompr-r85/4885/48852016/Jenga%25AE+giant%25u2122+premium+jeu+de+bois+franc.jpg) tiendra. -Peut-être que les choses auraient été différentes si vous aviez pris un peu plus de temps pour réfléchir à la manière dont vous modifieriez le code dans le futur. Peut-être que vous auriez *cleané* le code. Vous auriez peut-être expliqué le [*magic number*](https://en.wikipedia.org/wiki/Magic_number_(programming)) (pourquoi diviser par 8.2?). Peut-être que les effets de bords auraient été commentés. *Anyway* ce n’est pas important pour le moment, le code *marche*. +Peut-être que les choses auraient été différentes si vous aviez pris un peu plus de temps pour réfléchir à la manière dont vous modifieriez le code dans le futur. Peut-être que vous auriez _cleané_ le code. Vous auriez peut-être expliqué le [_magic number_]() (pourquoi diviser par 8.2?). Peut-être que les effets de bords auraient été commentés. _Anyway_ ce n’est pas important pour le moment, le code _marche_. -Mais ça, ça ne s'appliquait pas à moi. Je pouvais revenir dans mon code, lire le commentaire plein de sagesse que j'avais écrit et me rappeler ce que «i», «j» et «k» représentaient dans la matrice «X». Je crée des fonctions, donc je [*ne répète pas de code*](https://en.wikipedia.org/wiki/Don%27t_repeat_yourself). J'ai des *smell tests* qui me permettent de savoir si je casse quelque chose. Non vraiment, j'ai maitrisé l'art de la programmation. +Mais ça, ça ne s'appliquait pas à moi. Je pouvais revenir dans mon code, lire le commentaire plein de sagesse que j'avais écrit et me rappeler ce que «i», «j» et «k» représentaient dans la matrice «X». Je crée des fonctions, donc je [_ne répète pas de code_](https://en.wikipedia.org/wiki/Don%27t_repeat_yourself). J'ai des _smell tests_ qui me permettent de savoir si je casse quelque chose. Non vraiment, j'ai maitrisé l'art de la programmation. ## Jusqu'au jour où. -Je travaillais dans une [startup techno](https://www.xpertsea.com/) et mon équipe travaillait sur un modèle de détection de crevettes. On venait de milieux bien différents. Tout le monde était des ingénieurs logiciels sauf moi; j'étais un actuaire, le plus compétent en statistiques de la *gang*. On avait été mis les uns contre les autres pour résoudre ce problème de crevettes. *Plot Twist*. C'est pas moi qui avais les bons modèles. +Je travaillais dans une [startup techno](https://www.xpertsea.com/) et mon équipe travaillait sur un modèle de détection de crevettes. On venait de milieux bien différents. Tout le monde était des ingénieurs logiciels sauf moi; j'étais un actuaire, le plus compétent en statistiques de la _gang_. On avait été mis les uns contre les autres pour résoudre ce problème de crevettes. _Plot Twist_. C'est pas moi qui avais les bons modèles. -Ça l'avait pourtant bien commencé. J'ai rapidement construit un script qui me permettait de récupérer les données d'une source non structurée et de les convertir en un format structuré. Je lançais des diagnostics pour trouver des valeurs aberrantes ou de la colinéarité entre les caractéristiques. Une fois les données nettoyées, un autre script générait un modèle de régression utilisant un algorithme *stepwise*. Le modèle génèrerait des prévisions et les enregistrerait dans un fichier. Le dernier script faisait des graphiques et des analyses de base. Le day-to-day d'un *data scientist*. Le rapport était terminé et je pouvais rapidement donner mes résultats à l'équipe. +Ça l'avait pourtant bien commencé. J'ai rapidement construit un script qui me permettait de récupérer les données d'une source non structurée et de les convertir en un format structuré. Je lançais des diagnostics pour trouver des valeurs aberrantes ou de la colinéarité entre les caractéristiques. Une fois les données nettoyées, un autre script générait un modèle de régression utilisant un algorithme _stepwise_. Le modèle génèrerait des prévisions et les enregistrerait dans un fichier. Le dernier script faisait des graphiques et des analyses de base. Le day-to-day d'un _data scientist_. Le rapport était terminé et je pouvais rapidement donner mes résultats à l'équipe. -Le problème est venu par la suite, lorsqu'on a dû évaluer l'évolution du modèle. L'analyse est devenue une affaire hebdomadaire, puis quotidienne. Chaque jour, l'analyse devenait de plus en plus compliquée et le *dataset* grossissait et devenait de plus en plus étrange. Il fallait découper les données en fonction de certains fournisseurs de crevettes, âge, taille moyenne, pays et qualité de l'eau - *name it*. On faisait des tests en changeant le traitement des images, on a ajouté des attributs à l'infini, on a utilisé plusieurs mesures de performance. On a corrigé les données. C'est là que j'ai pogné mon mur. Le gars en génie logiciel faisait ses analyses quelques (plusieurs) heures avant moi, tous les jours. +Le problème est venu par la suite, lorsqu'on a dû évaluer l'évolution du modèle. L'analyse est devenue une affaire hebdomadaire, puis quotidienne. Chaque jour, l'analyse devenait de plus en plus compliquée et le _dataset_ grossissait et devenait de plus en plus étrange. Il fallait découper les données en fonction de certains fournisseurs de crevettes, âge, taille moyenne, pays et qualité de l'eau - _name it_. On faisait des tests en changeant le traitement des images, on a ajouté des attributs à l'infini, on a utilisé plusieurs mesures de performance. On a corrigé les données. C'est là que j'ai pogné mon mur. Le gars en génie logiciel faisait ses analyses quelques (plusieurs) heures avant moi, tous les jours. ## Pourquoi ça marche pas? Mon instinct de résolution de problème s'est fait interpeler. Comment est-ce que je fais pour être aussi rapide que mon collègue? En comprenant mieux ce qu'il fait. -La première chose que j'ai vue est que l'étape de prétraitement a été effectuée chaque nuit par un script automatisé. La deuxième chose que j’ai vue était sa [cache](https://en.wikipedia.org/wiki/Cache_ (informatique)), où toutes les données étaient prêtes pour l'analyse et les valeurs intermédiaires étaient déjà précalculées (pour le regroupement des autres analyses). La troisième chose était la partie modélisation, où tout était si bien attaché ensemble qu'il pouvait multithreader toutes ses analyses de données en une seule opération. Le pire dans tout ça - c'est que tout était prêt à 9 heures du matin quand il arrivait. Disons que ça lui donnait une pas pire longueur d'avance. +La première chose que j'ai vue est que l'étape de prétraitement a été effectuée chaque nuit par un script automatisé. La deuxième chose que j’ai vue était sa [cache](https://en.wikipedia.org/wiki/Cache_ 'informatique'), où toutes les données étaient prêtes pour l'analyse et les valeurs intermédiaires étaient déjà précalculées (pour le regroupement des autres analyses). La troisième chose était la partie modélisation, où tout était si bien attaché ensemble qu'il pouvait multithreader toutes ses analyses de données en une seule opération. Le pire dans tout ça - c'est que tout était prêt à 9 heures du matin quand il arrivait. Disons que ça lui donnait une pas pire longueur d'avance. Je lui ai demandé s'il pouvait me filer un ou deux morceaux de code pour les intégrer à mes scripts. Ma fierté était suffisamment atteinte que je suis retourné seul à mon bureau pour régler ça. C'était un morceau de code relativement gros, alors je m'attendais à un effort proportionné pour tout brancher et tout tester. J'ai ouvert le code et j'ai pu localiser rapidement les lignes qui nécessiteraient des modifications. Je me lance. la musique de touches résonnait dans mes oreilles - oui, j'avais déjà mon [clavier mécanique](http://www.wasdkeyboards.com/index.php/products/code-keyboard/code-87-key-mechanical-keyboard.html) à ce moment. Ça ne m'a pris qu'environ une demi-heure pour tout faire. J'ai tout fait fonctionner sans effets secondaires inattendus la première fois que j'ai lancé le script. @@ -45,13 +46,13 @@ Wow. Quelle excellente modification par un excellent programmeur. ## Attends un peu... -C'est le moment où l'illumination est arrivée. C'est pas moi qui ai fait un excellent travail: c'est mon coéquipier. Il m'a remis un code tellement propre que n'importe qui pouvait rapidement modifier quelque chose sans *bug*. Tout était tellement clair qu'aucun commentaire n’était nécessaire, pas de chiffres magiques, pas de valeurs *hardcodé*. Le code avait la longueur verticale et horizontale parfaite et mon thème *solarized* faisait en sorte que le code ressemblait à une oeuvre d'art. J'ai connu, ce qu'à l'époque je pensais être, le [Saint Graal du bon code](https://coding2fun.wordpress.com/2017/02/08/how-to-design-reliable-scalable-and-maintainable-applications/). +C'est le moment où l'illumination est arrivée. C'est pas moi qui ai fait un excellent travail: c'est mon coéquipier. Il m'a remis un code tellement propre que n'importe qui pouvait rapidement modifier quelque chose sans _bug_. Tout était tellement clair qu'aucun commentaire n’était nécessaire, pas de chiffres magiques, pas de valeurs _hardcodé_. Le code avait la longueur verticale et horizontale parfaite et mon thème _solarized_ faisait en sorte que le code ressemblait à une oeuvre d'art. J'ai connu, ce qu'à l'époque je pensais être, le [Saint Graal du bon code](https://coding2fun.wordpress.com/2017/02/08/how-to-design-reliable-scalable-and-maintainable-applications/). ## Des solutions différentes -Tout d'abord, il y avait des choses que mon coéquipier savait que je ne savais pas que je ne savais pas. Des choses comme la pipeline de données, les [patrons](https://sourcemaking.com/design_patterns) et [principes](https://en.wikipedia.org/wiki/SOLID) de conception, les tests unitaires, le[code propre](https://en.wikipedia.org/wiki/Worship), l'architecture logicielle, etc. Il s'agit de la loi normale pour l'actuaire, le $ (X'X)^{-1} $ du statisticien. La fondation sur laquelle tout est construit. De toute évidence, mon *hacking* informatique et mon parcours scolaire ne m'avaient pas préparé à ça. +Tout d'abord, il y avait des choses que mon coéquipier savait que je ne savais pas que je ne savais pas. Des choses comme la pipeline de données, les [patrons](https://sourcemaking.com/design_patterns) et [principes](https://en.wikipedia.org/wiki/SOLID) de conception, les tests unitaires, le[code propre](https://en.wikipedia.org/wiki/Worship), l'architecture logicielle, etc. Il s'agit de la loi normale pour l'actuaire, le $ (X'X)^{-1} $ du statisticien. La fondation sur laquelle tout est construit. De toute évidence, mon _hacking_ informatique et mon parcours scolaire ne m'avaient pas préparé à ça. -Mon coéquipier avait une vision holistique du problème. J'étais trop occupé *à faire marcher les choses* pendant que mon coéquipier était occupé à trouver le *meilleur moyen* de faire marcher les choses. Cette différence subtile est cruciale. Une bonne solution permet de faire fonctionner quelque chose maintenant. **La** bonne solution permet de faire fonctionner quelque chose ET de considérer l'utilisateur futur. Même si cela signifie prendre plus de temps pour prendre du recul et se remettre dans la bonne direction. +Mon coéquipier avait une vision holistique du problème. J'étais trop occupé _à faire marcher les choses_ pendant que mon coéquipier était occupé à trouver le _meilleur moyen_ de faire marcher les choses. Cette différence subtile est cruciale. Une bonne solution permet de faire fonctionner quelque chose maintenant. **La** bonne solution permet de faire fonctionner quelque chose ET de considérer l'utilisateur futur. Même si cela signifie prendre plus de temps pour prendre du recul et se remettre dans la bonne direction. De toute évidence, aucune solution n’aurait pu (ou dû) semer toutes les graines des folies qui ont suivi la première demande. Mais mon obstination à trouver une solution rapide, recyclant ma première approche sans reconsidérer le possible changement de paradigme du problème, m'a nui à long terme. @@ -59,4 +60,4 @@ De toute évidence, aucune solution n’aurait pu (ou dû) semer toutes les grai Je me suis inscrit à un certificat en génie logiciel pour bâtir une base plus solide. Cela m’a permis d’ouvrir une énorme boîte de Pandore de laquelle ont surgi des idées et des concepts impressionnants. Je crois que je suis un meilleur programmeur que j'étais et que je dispose des outils pour être meilleur demain. -Cet article est le premier de plusieurs dans lesquels je partagerai les outils que j'ai trouvés dans cette boîte de Pandore. Ces articles seront à moitié philosophiques et à moitié techniques. Ils seront centrés sur les bonnes pratiques en matière de génie logiciel, tout en partageant mon expérience avec du code (vraiment) à chier. Du code à chier qui a été écrit par moi, bien sûr. \ No newline at end of file +Cet article est le premier de plusieurs dans lesquels je partagerai les outils que j'ai trouvés dans cette boîte de Pandore. Ces articles seront à moitié philosophiques et à moitié techniques. Ils seront centrés sur les bonnes pratiques en matière de génie logiciel, tout en partageant mon expérience avec du code (vraiment) à chier. Du code à chier qui a été écrit par moi, bien sûr. diff --git a/content/blog/2019-05-21-argparse-package/index.fr.Rmd b/content/blog/2019-05-21-argparse-package/index.fr.Rmd index 994d33ad0..757322461 100644 --- a/content/blog/2019-05-21-argparse-package/index.fr.Rmd +++ b/content/blog/2019-05-21-argparse-package/index.fr.Rmd @@ -9,6 +9,7 @@ description: "Utiliser le paquetage argparse pour faciliter l'appel de scripts R output: html_document featured: "argparse-package.png" featuredpath: "img/headers/" +aliases: [/blog/2019-05-21-argparse-package/optparse-package/] --- ```{r setup, include=FALSE} diff --git a/content/blog/2019-05-21-argparse-package/index.fr.html b/content/blog/2019-05-21-argparse-package/index.fr.html index b40a1c490..84f900cdd 100644 --- a/content/blog/2019-05-21-argparse-package/index.fr.html +++ b/content/blog/2019-05-21-argparse-package/index.fr.html @@ -1,65 +1,144 @@ --- -title: "argparse: un outil méconnu" -author: "Stéphane Caron" +title: 'argparse: un outil méconnu' +author: 'Stéphane Caron' date: '2020-01-03' slug: optparse-package -categories: ["R"] +categories: ['R'] type: post description: "Utiliser le paquetage argparse pour faciliter l'appel de scripts R." output: html_document -featured: "argparse-package.png" -featuredpath: "img/headers/" +featured: 'argparse-package.png' +featuredpath: 'img/headers/' +aliases: [/blog/2019-05-21-argparse-package/optparse-package/] --- - - -

Avez-vous déjà eu à lancer un programme avec différents paramètres? J’imagine que oui … Une manière de faire serait de se définir des paramètres en début de programme, les changer manuellement, sauvegarder le programme et relancer de nouveau. Vous imaginez bien que cela n’est pas agréable si on veut tester 20 combinaisons de paramètres différents. Une autre manière pourrait être de se définir un fichier de configurations, mais encore là on se retrouve face au même problème de devoir définir plusieurs fichiers de configurations ou bien d’avoir un programme qui itère sur les configurations du fichier. Bon, on s’approche de quelque chose qui devient de moins en moins laborieux. Pour ma part, j’aime bien l’idée d’avoir un programme paramétrable et définir les différents appels dans un autre fichier de lancement.

-

Dans les derniers mois, j’ai été confronté à 2 défis qui m’ont éventuellement permis de découvrir ce concept de paramétriser un programme, et ce, via un paquetage R. Dans un premier temps, je devais intégrer l’appel de programmes R à l’intérieur d’un fichier de ligne de commande ou un shell script (un fichier avec l’extension .sh) dans lequel je devais passer différents arguments qui allaient être utilisés par le programme. Dans un deuxième temps, je devais lancer un programme R, avec différents paramètres, à l’aide d’une instance en ligne (par exemple AWS), pour laquelle je n’avais pas d’interface graphique. Dans les deux cas, je devais faire l’appel d’un programme R à partir de la ligne de commande, ce qui est d’ailleurs possible avec la commande Rscript. Par contre, je devais également passer des arguments à ce programme lors de l’appel à la ligne de commande. C’est à ce moment que j’ai fais la découverte du paquetage argparse, un outil méconnu par plusieurs et qui offre des fonctionnalités intéressantes.

+

+ Avez-vous déjà eu à lancer un programme avec différents paramètres? J’imagine que oui … Une manière de faire + serait de se définir des paramètres en début de programme, les changer manuellement, sauvegarder le + programme et relancer de nouveau. Vous imaginez bien que cela n’est pas agréable si on veut tester 20 + combinaisons de paramètres différents. Une autre manière pourrait être de se définir un fichier de + configurations, mais encore là on se retrouve face au même problème de devoir définir plusieurs fichiers de + configurations ou bien d’avoir un programme qui itère sur les configurations du fichier. Bon, on s’approche + de quelque chose qui devient de moins en moins laborieux. Pour ma part, j’aime bien l’idée d’avoir un + programme paramétrable et définir les différents appels dans un autre fichier de lancement. +

+

+ Dans les derniers mois, j’ai été confronté à 2 défis qui m’ont éventuellement permis de découvrir ce concept + de paramétriser un programme, et ce, via un paquetage R. Dans un premier temps, je devais intégrer l’appel + de programmes R à l’intérieur d’un fichier de ligne de commande ou un shell script (un fichier avec + l’extension .sh) dans lequel je devais passer différents arguments qui allaient être utilisés + par le programme. Dans un deuxième temps, je devais lancer un programme R, avec différents paramètres, à + l’aide d’une instance en ligne (par exemple AWS), pour laquelle je n’avais pas d’interface + graphique. Dans les deux cas, je devais faire l’appel d’un programme R à partir de la ligne de commande, ce + qui est d’ailleurs possible avec la commande Rscript. Par contre, je devais également passer + des arguments à ce programme lors de l’appel à la ligne de commande. C’est à ce moment que j’ai fais la + découverte du paquetage argparse, + un outil méconnu par plusieurs et qui offre des fonctionnalités intéressantes. +

-

argparse et la ligne de commande

-

Comme vous l’avez probablement deviné dans l’introduction, argparse est un paquetage R qui permet de faciliter l’appel de programmes R à partir de la ligne de commande. Pour être plus précis, argparse permet de créer une interface de ligne de commande (command line interface ou CLI). Cette interface agit comme une sorte de pont (ou moyen de communication) entre l’appel d’un programme via la ligne de commande et les opérations effectuées à l’intérieur de ce programme.

-

Pourquoi appeler un programme R de la ligne de commande alors qu’on peut le lancer directement de la majorité des IDE comme RStudio ou même à partir de R directement? Plusieurs exemples pourraient être cités, les deux mentionnés en introduction sont pour moi des applications courantes, surtout lorsqu’on doit utiliser des ressources en ligne comme des instances AWS pour plus de puissance de calculs. Ces instances sont souvent dépourvues d’interface graphique, ce qui fait en sorte que nous nous retrouvons souvent devant le terrifiant écran noir du terminal.

+

argparse et la ligne de commande

+

+ Comme vous l’avez probablement deviné dans l’introduction, argparse est un paquetage R qui + permet de faciliter l’appel de programmes R à partir de la ligne de commande. Pour être plus précis, + argparse permet de créer une interface de ligne de commande (command line interface + ou CLI). Cette interface agit comme une sorte de pont (ou moyen de communication) entre l’appel + d’un programme via la ligne de commande et les opérations effectuées à l’intérieur de ce programme. +

+

+ Pourquoi appeler un programme R de la ligne de commande alors qu’on peut le lancer directement de la + majorité des IDE comme RStudio ou même à partir de R directement? Plusieurs exemples pourraient + être cités, les deux mentionnés en introduction sont pour moi des applications courantes, surtout + lorsqu’on doit utiliser des ressources en ligne comme des instances AWS pour plus de puissance de + calculs. Ces instances sont souvent dépourvues d’interface graphique, ce qui fait en sorte que nous nous + retrouvons souvent devant le terrifiant écran noir du terminal. +

-

Fonctionnement

-

Le paquetage argparse est en fait un wrapper à la librairie Python du même nom. D’autres paquetages R, comme optparse, fonctionnent de manière similaire. L’objectif de cet article n’est pas de vanter un paquetage plus qu’un autre, mais plutôt d’illustrer le concept général en utilisant comme exemple le paquetage argparse. La création d’une interface de ligne de commande avec ce paquetage est très simple, les étapes sont généralement :

-
    -
  1. Définir les arguments pouvant être appelés de la ligne de commande
  2. -
  3. Parser les arguments et les stocker dans un objet (habituellement une liste)
  4. -
  5. Utiliser les éléments de cette liste dans le programme
  6. -
-

Les étapes 2 et 3 sont sont assez simples à réaliser. L’étape la plus cruciale est de bien définir les arguments (étape 1). Pour se faire, il y a quelques éléments essentiels à prendre en compte. Les prochaines sections mettent en lumière ces considérations.

-
-

Initialiser l’objet Parser

-

Tout d’abord, avant même de définir les arguments, il faut attacher le paquetage argparse et initialiser l’objet de type Parser. Cet objet est le coeur de l’interface entre la ligne de commande et le programme et permettra de définir et stocker des arguments. Une fois cet objet initialisé, nous pourrons ajouter des arguments à celui-ci, ce que nous verrons dans les prochaines sections.

-
library(argparse)
+	

Fonctionnement

+

+ Le paquetage argparse est en fait un wrapper à la librairie Python du même nom. + D’autres paquetages R, comme + optparse, fonctionnent de + manière similaire. L’objectif de cet article n’est pas de vanter un paquetage plus qu’un autre, mais + plutôt d’illustrer le concept général en utilisant comme exemple le paquetage argparse. La + création d’une interface de ligne de commande avec ce paquetage est très simple, les étapes sont + généralement : +

+
    +
  1. Définir les arguments pouvant être appelés de la ligne de commande
  2. +
  3. Parser les arguments et les stocker dans un objet (habituellement une liste)
  4. +
  5. Utiliser les éléments de cette liste dans le programme
  6. +
+

+ Les étapes 2 et 3 sont sont assez simples à réaliser. L’étape la plus cruciale est de bien définir les + arguments (étape 1). Pour se faire, il y a quelques éléments essentiels à prendre en compte. Les + prochaines sections mettent en lumière ces considérations. +

+
+

Initialiser l’objet Parser

+

+ Tout d’abord, avant même de définir les arguments, il faut attacher le paquetage + argparse et initialiser l’objet de type Parser. Cet objet est le coeur de + l’interface entre la ligne de commande et le programme et permettra de définir et stocker des arguments. + Une fois cet objet initialisé, nous pourrons ajouter des arguments à celui-ci, ce que nous verrons dans + les prochaines sections. +

+
library(argparse)
 parser <- ArgumentParser(description = "Simuler des distributions normales")
-
-
-

Nommer les arguments

-

Maintenant que l’objet Parser est défini, il faut définir et nommer les arguments qui pourront être appelés à la ligne de commande. Pour ajouter ces arguments à notre objet, on utilise la méthode add_argument(). Il existe deux sortes d’arguments: les argument positionnels et les arguments optionnels. Dans le premier cas, ces arguments sont obligatoires et doivent être appelés dans un ordre précis alors que dans le deuxième cas, ils sont facultatifs et peuvent être appelés dans n’importe quel ordre, tant qu’ils sont nommés dans l’appel. Voici un exemple simple d’ajout de ces 2 sortes d’arguments:

-
parser$add_argument("n_dist", type = "integer")
+	
+
+

Nommer les arguments

+

+ Maintenant que l’objet Parser est défini, il faut définir et nommer les arguments qui + pourront être appelés à la ligne de commande. Pour ajouter ces arguments à notre objet, on utilise la + méthode add_argument(). Il existe deux sortes d’arguments: les argument positionnels et les + arguments optionnels. Dans le premier cas, ces arguments sont obligatoires et doivent être appelés dans + un ordre précis alors que dans le deuxième cas, ils sont facultatifs et peuvent être appelés dans + n’importe quel ordre, tant qu’ils sont nommés dans l’appel. Voici un exemple simple d’ajout de ces 2 + sortes d’arguments: +

+
parser$add_argument("n_dist", type = "integer")
 parser$add_argument("-m", "--mean", type = "double", default = 0)
-

Les arguments optionnels sont généralement identifiés par le préfixe - alors que les autres seront considérés comme positionnels. L’option required permet également de spécifier les arguments qui ne peuvent pas être omis lors de l’appel.

-

Il est possible de spécifier le type de valeur de l’argument grâce à l’option type qui peut prendre les types suivants:

-
    -
  • “double”
  • -
  • “character”
  • -
  • “logical”
  • -
  • “integer”
  • -
-

Les arguments optionnels doivent être accompagnés d’une valeur par défaut. Notez que ceux-ci sont parfois nommés de 2 manières, avec un seul trait d’union (-m) pour l’abréviation courte et deux (--mean) pour le nom complet. Il n’est pas obligatoire de mettre les deux, mais cela peut améliorer la clarté de l’appel.

-
-
-

Définir l’aide

-

Une fonctionnalité intéressante avec les interfaces de ligne de commande comme celle du paquetage argparse est que l’on peut généralement définir une aide pour l’appel de chaque argument pouvant être appelé par le programme. Cela permet de documenter un programme en résumant son objectif et en résumant son mode d’usage. Cette aide peut être affichée en appelant l’argument -h ou --help au programme. Par exemple,

-
parser <- ArgumentParser(description = "Simuler des distributions normales")
+		

+ Les arguments optionnels sont généralement identifiés par le préfixe - alors que les autres + seront considérés comme positionnels. L’option required permet également de spécifier les + arguments qui ne peuvent pas être omis lors de l’appel. +

+

+ Il est possible de spécifier le type de valeur de l’argument grâce à l’option type qui peut + prendre les types suivants: +

+
    +
  • “double”
  • +
  • “character”
  • +
  • “logical”
  • +
  • “integer”
  • +
+

+ Les arguments optionnels doivent être accompagnés d’une valeur par défaut. Notez que ceux-ci sont + parfois nommés de 2 manières, avec un seul trait d’union (-m) pour l’abréviation courte et + deux (--mean) pour le nom complet. Il n’est pas obligatoire de mettre les deux, mais cela + peut améliorer la clarté de l’appel. +

+
+
+

Définir l’aide

+

+ Une fonctionnalité intéressante avec les interfaces de ligne de commande comme celle du paquetage + argparse est que l’on peut généralement définir une aide pour l’appel de chaque argument + pouvant être appelé par le programme. Cela permet de documenter un programme en résumant son objectif et + en résumant son mode d’usage. Cette aide peut être affichée en appelant l’argument -h ou + --help au programme. Par exemple, +

+
parser <- ArgumentParser(description = "Simuler des distributions normales")
 parser$add_argument("n_dist", type = "integer",
                     help = "nombre de distributions simulées")
 parser$add_argument("-m", "--mean", type = "double", default = 0,
                     help = "moyenne pour chaque distribution normale [défault: %(default)s, type: %(type)s]")
 parser$print_help()
-
## usage: /Library/Frameworks/R.framework/Versions/3.6/Resources/library/blogdown/scripts/render_page.R
+		
## usage: /Library/Frameworks/R.framework/Versions/3.6/Resources/library/blogdown/scripts/render_page.R
 ##        [-h] [-m MEAN] n_dist
 ## 
 ## Simuler des distributions normales
@@ -71,22 +150,37 @@ 

Définir l’aide

## -h, --help show this help message and exit ## -m MEAN, --mean MEAN moyenne pour chaque distribution normale [défault: 0, ## type: float]
-

Notez que la valeur par défaut et le type peuvent être incorporés dans l’aide (voir le code ci-dessus).

-
-
-

Fonctionnalités supplémentaires

-

Ces quelques options devraient vous permettre de créer une interface de ligne de commande simple d’utilisation et couvrant la majorité de vos besoins. Toutefois, notez que le paquetage possède également plusieurs autres fonctionnalités intéressantes comme regrouper des arguments, hériter des propriétés d’autres arguments parents, améliorer l’affichage de l’aide, etc.

-
-
-

Une fois les arguments définis

-

Une fois les arguments bien définis, il ne reste plus qu’à parser les arguments passés à ligne de commande et les utiliser dans le programme en utilisant la fonction parse_args():

-
args <- parser$parse_args()
-
+

+ Notez que la valeur par défaut et le type peuvent être incorporés dans l’aide (voir le code ci-dessus). +

+
+
+

Fonctionnalités supplémentaires

+

+ Ces quelques options devraient vous permettre de créer une interface de ligne de commande simple + d’utilisation et couvrant la majorité de vos besoins. Toutefois, notez que le paquetage possède + également plusieurs autres fonctionnalités intéressantes comme regrouper des arguments, hériter des + propriétés d’autres arguments parents, améliorer l’affichage de l’aide, etc. +

+
+
+

Une fois les arguments définis

+

+ Une fois les arguments bien définis, il ne reste plus qu’à parser les arguments passés à ligne + de commande et les utiliser dans le programme en utilisant la fonction parse_args(): +

+
args <- parser$parse_args()
+
-

Exemple

-

Voici un exemple de programme où on veut simuler un certain nombre de lois normales et estimer la moyenne et l’écart-type de la distribution avec ces simulations. Supposons en plus qu’on veuille pouvoir spécifier les paramètres de ces lois normales et sauvegarder un graphique illustrant la densité estimée comparativement à la vraie densité.

-
library(argparse)
+	

Exemple

+

+ Voici un exemple de programme où on veut simuler un certain nombre de lois normales et estimer la moyenne + et l’écart-type de la distribution avec ces simulations. Supposons en plus qu’on veuille pouvoir spécifier + les paramètres de ces lois normales et sauvegarder un graphique illustrant la densité estimée + comparativement à la vraie densité. +

+
library(argparse)
 library(ggplot2)
 
 
@@ -138,15 +232,28 @@ 

Exemple

if (args$graph_save) { ggsave(args$path_graph, plot) }
-

Voici un exemple d’appel (voir figure) et de résultat effectués à la ligne de commande en utilisant ce programme :

-
-Exemple d'appel du programme à partir de la ligne de commande. -

-Figure 1: Exemple d’appel du programme à partir de la ligne de commande. -

-
+

+ Voici un exemple d’appel (voir figure) et de résultat effectués à la ligne de + commande en utilisant ce programme : +

+
+ + Exemple d'appel du programme à partir de la ligne de commande. +

Figure 1: Exemple d’appel du programme à partir de la ligne de commande.

+
-

Conclusion

-

Pour davantage d’informations sur le fonctionnement de argparse et des différentes options qu’il offre, vous pouvez consulter le dépôt du paquetage et aussi la documentation complète en ligne de la même librairie Python. J’espère que ce court article vous aura permis de comprendre l’essentiel quant au fonctionnement des interfaces de ligne de commande en R et leur utilité.

+

Conclusion

+

+ Pour davantage d’informations sur le fonctionnement de argparse et des différentes options + qu’il offre, vous pouvez consulter le dépôt du + paquetage et aussi la documentation complète + en ligne de la même librairie Python. + J’espère que ce court article vous aura permis de comprendre l’essentiel quant au fonctionnement des + interfaces de ligne de commande en R et leur utilité. +

diff --git a/content/blog/2019-06-16-agregation-classements/index.fr.md b/content/blog/2019-06-16-agregation-classements/index.fr.md index d5ef53f42..ea0105b43 100644 --- a/content/blog/2019-06-16-agregation-classements/index.fr.md +++ b/content/blog/2019-06-16-agregation-classements/index.fr.md @@ -9,6 +9,7 @@ tags: ['Analytique de sports'] description: '' featured: 'agregation-cover.png' featuredpath: 'img/headers/' +aliases: [/blog/2019-06-16-agregation-classements/agrégation-simple-de-classements/] --- Il y a quelques jours, des membres de la communauté .Layer se sont réunis dans un chalet autour d'une thématique: l'analyse de données sportives. Lors de ce week-end, nous avons effleuré trois projets reliant sports et analyse de données: diff --git a/content/blog/2019-12-19-recap-2019/index.fr.md b/content/blog/2019-12-19-recap-2019/index.fr.md index 2b7da9d97..94ad0e83f 100644 --- a/content/blog/2019-12-19-recap-2019/index.fr.md +++ b/content/blog/2019-12-19-recap-2019/index.fr.md @@ -4,49 +4,49 @@ author: Samuel Perreault et David Beauchemin date: '2020-01-14' slug: recap-2019 type: post -categories: ["Nouvelles .Layer"] +categories: ['Nouvelles .Layer'] tags: [] -description: "" -featured: "recap-2019-cover.png" -featuredpath: "img/headers/" +description: '' +featured: 'recap-2019-cover.png' +featuredpath: 'img/headers/' +aliases: [/blog/2019-12-19-recap-2019/recap-2019/] --- -Vendredi le 17 décembre 2019 marquait la fin de 2019 de .Layer avec la deuxième édition du Noël *recap*, cette fois-ci au Noctem sur Charest/du Parvis à Québec. +Vendredi le 17 décembre 2019 marquait la fin de 2019 de .Layer avec la deuxième édition du Noël _recap_, cette fois-ci au Noctem sur Charest/du Parvis à Québec. Un gros merci à JT Baï d'avoir pris le temps d'organiser l'événement! à Sté et JC Yelle pour la présentation et à tous ceux qui se sont pointés la bean. Mine de rien, 2019 fut une année chargée pour .Layer. -Voici donc un petit *recap* pour les intéressé(e)s. +Voici donc un petit _recap_ pour les intéressé(e)s. # Officiellement enregistré! L'année a commencé en force avec l'enregistrement, en février, de .Layer en tant qu'organisme sans but lucratif (OSBL). -Officiellement, le nom de l'OSBL est *RAMQ* (est bonne quand même non?) pour Regroupement en Apprentissage Machine de Québec. -Le pseudonyme *.Layer* est le nom d'exercice. +Officiellement, le nom de l'OSBL est _RAMQ_ (est bonne quand même non?) pour Regroupement en Apprentissage Machine de Québec. +Le pseudonyme _.Layer_ est le nom d'exercice. Le comité administratif est composé de - - [Stéphane Caron](https://github.com/stecaron) -- Président - - [David Beauchemin](https://github.com/davebulaval) -- Vice-Président affaires externes - - [Jean-Thomas Baillargeon](https://github.com/jtbai) -- Trésorier - - [Nicolas Garneau](https://github.com/ngarneau) -- Secrétaire - - [Jean-Christophe Yelle](https://github.com/jcyelle) -- Vice-Président communications - -La CA a pris soin de créer une [charte](https://github.com/dot-layer/charte-osbl) pour l'OSBL. -Pour 2020, l'objectif est de mettre en place un processus d'adhésion de membres dits *actifs*, qui incluerait potentiellement une (petite!) cotisation. +- [Stéphane Caron](https://github.com/stecaron) -- Président +- [David Beauchemin](https://github.com/davebulaval) -- Vice-Président affaires externes +- [Jean-Thomas Baillargeon](https://github.com/jtbai) -- Trésorier +- [Nicolas Garneau](https://github.com/ngarneau) -- Secrétaire +- [Jean-Christophe Yelle](https://github.com/jcyelle) -- Vice-Président communications +La CA a pris soin de créer une [charte](https://github.com/dot-layer/charte-osbl) pour l'OSBL. +Pour 2020, l'objectif est de mettre en place un processus d'adhésion de membres dits _actifs_, qui incluerait potentiellement une (petite!) cotisation. # Comité blog En plus du CA, on a vu la naissance d'un comité pour prendre en charge le [blog](https://www.dotlayer.org/fr/). Celui-ci est composé de [Christopher Blier-Wong](https://github.com/chblw), [Guillaume Chevalier](https://github.com/guillaume-chevalier), [Annie Deshaies](https://github.com/AnnieDeshaies), [Samuel Perreault](https://github.com/samperochkin) et Simon Valois, qui se chargeront de maintenir le site en vie, de l'améliorer, et d'accompagner les contributeurs avec un service d'édition. -Le site a été *upgradé* pas mal en 2019, surtout son allure (merci Annie), et 2020 s'annonce l'année la plus active en termes d'articles : déjà un article sorti concernant [argparse](https://www.dotlayer.org/fr/blog/2019-05-21-argparse-package/optparse-package/) et un autre *pending*. -On compte aussi l'utiliser pour garder à jour les membres et *followers* sur ce qui se passe avec les réalisations .Layeuriennes (comme je fais en ce moment quoi). +Le site a été _upgradé_ pas mal en 2019, surtout son allure (merci Annie), et 2020 s'annonce l'année la plus active en termes d'articles : déjà un article sorti concernant [argparse](https://www.dotlayer.org/fr/blog/2019-05-21-argparse-package/optparse-package/) et un autre _pending_. +On compte aussi l'utiliser pour garder à jour les membres et _followers_ sur ce qui se passe avec les réalisations .Layeuriennes (comme je fais en ce moment quoi). -Le comité est ouvert à toutes sortes d'articles : français, anglais, vulgarisation de concepts (apprentissage machine, programmation, science des données, statistique, etc), expérience personnelle, présentation d'un projet, *name it*! Soyez pas timide. +Le comité est ouvert à toutes sortes d'articles : français, anglais, vulgarisation de concepts (apprentissage machine, programmation, science des données, statistique, etc), expérience personnelle, présentation d'un projet, _name it_! Soyez pas timide. # OpenLayer Euh oui, un podcast .Layeurien existe. C'est une initiative de [David Beauchemin](https://github.com/davebulaval), qui expose différentes facettes du domaine de l'apprentissage machine et de l'intelligence artificielle. -[OpenLayer](https://www.youtube.com/channel/UCB3tYpZ1ojiqAroyDN05Cyw/featured) publie des discussions de 55-75 minutes, au cours desquelles David questionne son invité sur son parcours qui l'a plongé dans le *ML*. +[OpenLayer](https://www.youtube.com/channel/UCB3tYpZ1ojiqAroyDN05Cyw/featured) publie des discussions de 55-75 minutes, au cours desquelles David questionne son invité sur son parcours qui l'a plongé dans le _ML_. Déjà 19 épisodes diffusés (et 3 de plus d'enregistrés) et plus de 3400 visionnements au total sur les plateformes YouTube, Spotify, Balado Québec, Itunes music, Google podcast. Pour 2020, la sortie d'épisodes techniques et un panel d'experts! @@ -57,34 +57,34 @@ Crédit photo : Jean-François Chaput, Mirego # Meetup Machine Learning Québec La gang de Meetup ML, [Nicolas Garneau](https://github.com/ngarneau) et [David Beauchemin](https://github.com/davebulaval) (le gars fait tout ou quoi?), fût encore une fois très active cette année. -On a eu droit à quatre meetups officiels et trois ateliers pratiques : +On a eu droit à quatre meetups officiels et trois ateliers pratiques : - - Meetup ML en vision chez XpertSea - - Meetup ML en finance chez Desjardins Lab Lévis - - Meetup ML chez Mirego - - Meetup MLQC 5@7 - maîtrise en IA chez LE CAMP - - Atelier d'introduction aux fondements du machine learning - - Atelier d'introduction aux réseaux de neurones convolutifs - - Atelier pratique Git +- Meetup ML en vision chez XpertSea +- Meetup ML en finance chez Desjardins Lab Lévis +- Meetup ML chez Mirego +- Meetup MLQC 5@7 - maîtrise en IA chez LE CAMP +- Atelier d'introduction aux fondements du machine learning +- Atelier d'introduction aux réseaux de neurones convolutifs +- Atelier pratique Git Nic et Dave tiennent à remercier les entreprises qui ont reçu Meetup Machine Learning Québec: XpertSea, Desjardins, Mirego et LE CAMP. De plus. un gros merci au [CRDM](https://crdm.ulaval.ca/) et au [Cercle Finance du Québec](https://cerclefinanceduquebec.com/) qui ont appuyé une ou plusieurs de nos initiatives. - -2020 s'annonce tout aussi occupée avec un meetup déjà *in the making*. + +2020 s'annonce tout aussi occupée avec un meetup déjà _in the making_. On vise aussi à incorporer plus de formules 5@7 et potentiellement un hackaton. À suivre, soyez à l'affût! # Chalet ML 2019 et 2020 -Le légendaire *Chalet .Layer*, une fin de semaine dans un Chalet avec -zuss des passionnés de *ML*, se tenait cette année à Stoneham en Août. +Le légendaire _Chalet .Layer_, une fin de semaine dans un Chalet avec +zuss des passionnés de _ML_, se tenait cette année à Stoneham en Août. L'ambiance décontractée était au rendez-vous pour les 20 .Léyeuriens présents. -Merci aux quatre présentateurs (JéDB, Nic, JTBaï, Camille - thx) d'avoir étanché notre soif de *ML*. -(*Next thing you know* Bute te sort un pain perdu du four. De la magie. Fallait être là.) +Merci aux quatre présentateurs (JéDB, Nic, JTBaï, Camille - thx) d'avoir étanché notre soif de _ML_. +(_Next thing you know_ Bute te sort un pain perdu du four. De la magie. Fallait être là.) Merci à tous pour l'effort général dans l'organisation. -On peut pas garantir que la compagnie va *double book* notre chalet pour ensuite nous dédommager avec un palace (un conte de fées quoi) comme en 2019, mais on peut dire (peut-on?) qu'en 2020 ça devrait être quelque part entre Montréal et Québec. +On peut pas garantir que la compagnie va _double book_ notre chalet pour ensuite nous dédommager avec un palace (un conte de fées quoi) comme en 2019, mais on peut dire (peut-on?) qu'en 2020 ça devrait être quelque part entre Montréal et Québec. Un comité sera créé en automne (prends pas ça pour du cash) pour veiller à son organisation. -Il faut, entre autres choses, déterminer si une nouvelle formule doit être adoptée. +Il faut, entre autres choses, déterminer si une nouvelle formule doit être adoptée. Accomoder plus de 25 personnes (la bouffe pi toute pi toute), ça commence à être un pas pire défi! ![Photo de groupe du Chalet 2k19](fig/chalet.fr.png) diff --git a/content/blog/2020-01-04-julia-boosting-trees/index.fr.md b/content/blog/2020-01-04-julia-boosting-trees/index.fr.md index 8a1b64944..1495b7c1b 100644 --- a/content/blog/2020-01-04-julia-boosting-trees/index.fr.md +++ b/content/blog/2020-01-04-julia-boosting-trees/index.fr.md @@ -1,37 +1,37 @@ --- -title: "Boosting Trees avec Julia" -author: "Jeremie Desgagne-Bouchard" -date: "2020-04-01" -slug: "julia-boosting-trees" -type: "post" -tags: ["Machine Learning", "Julia"] -featured: "julia-plot.png" -featuredpath: "img/headers/" +title: 'Boosting Trees avec Julia' +author: 'Jeremie Desgagne-Bouchard' +date: '2020-04-01' +slug: 'julia-boosting-trees' +type: 'post' +tags: ['Machine Learning', 'Julia'] +featured: 'julia-plot.png' +featuredpath: 'img/headers/' description: "Développement d'algorithmes from scratch" +aliases: [/blog/2020-01-04-julia-boosting-trees/julia-boosting-trees/] --- - -> Cet article a pour but d'exposer les principes clés permettant une implantation haute performance du gradient boosting trees en Julia, un langage réconciliant l'expressivité et la productivité qu'on retrouve en Python et en R et la performance de langages compilés comme le C et C++. +> Cet article a pour but d'exposer les principes clés permettant une implantation haute performance du gradient boosting trees en Julia, un langage réconciliant l'expressivité et la productivité qu'on retrouve en Python et en R et la performance de langages compilés comme le C et C++. Bien que les approches par réseaux de neurones accaparent une bonne partie de l'attention, l'importance des algorithmes reposant sur des arbres de décision ne peut être négligée. Ils continuent de se démarquer comme offrant la meilleure performance prédictive dans de nombreuses situations, particulièrement lorsqu'il s'agit de problèmes de régression ou de classification impliquant des données tabulaires. -Parmi les plus célèbres représentants de cette famille d'algorithmes, on compte [XGBoost](https://xgboost.readthedocs.io/en/latest/), [LightGBM](https://lightgbm.readthedocs.io/en/latest/) et [CatBoost](https://catboost.ai/). Si ces dernières implantations sont relativement récentes (2014, 2016 et 2017), l'idée avait été développée depuis déjà quelques années puisqu'on la retrouve dès 2001 dans le désormais classique [Elements of Statistical Learning](https://web.stanford.edu/~hastie/ElemStatLearn/). +Parmi les plus célèbres représentants de cette famille d'algorithmes, on compte [XGBoost](https://xgboost.readthedocs.io/en/latest/), [LightGBM](https://lightgbm.readthedocs.io/en/latest/) et [CatBoost](https://catboost.ai/). Si ces dernières implantations sont relativement récentes (2014, 2016 et 2017), l'idée avait été développée depuis déjà quelques années puisqu'on la retrouve dès 2001 dans le désormais classique [Elements of Statistical Learning](https://web.stanford.edu/~hastie/ElemStatLearn/). -Il serait hasardeux d'apporter un diagnostic définitif sur ce qui a conduit à l'explosion de popularité de l'algorithme. L'intérêt pour l'apprentissage machine et le développement d'une approche compétitive à la modélisation via des plateformes comme [Kaggle](https://www.kaggle.com/) n'y sont sans doute pas étrangers. Un atout du gradient boosting est également sa rapidité: XGBoost apportait à sa sortie une réduction du temps d'entraînement de l'ordre de 10X par rapport aux implantations en R et Python existantes. +Il serait hasardeux d'apporter un diagnostic définitif sur ce qui a conduit à l'explosion de popularité de l'algorithme. L'intérêt pour l'apprentissage machine et le développement d'une approche compétitive à la modélisation via des plateformes comme [Kaggle](https://www.kaggle.com/) n'y sont sans doute pas étrangers. Un atout du gradient boosting est également sa rapidité: XGBoost apportait à sa sortie une réduction du temps d'entraînement de l'ordre de 10X par rapport aux implantations en R et Python existantes. -Dans un contexte d'utilisation commerciale, les enjeux de performance deviennent rapidement significatifs compte tenu des volumes de données impliqués. Le souci qu'on y accorde au sein de la nouvelle génération d'algorithmes n'est sans doute pas étranger à ces impératifs commerciaux. Aussi, lorsqu'il est question de performance, le coeur d'un algorithme est typiquement développé dans un langage compilé (C/C++), bien que l'utilisateur interagit le plus souvent avec celui-ci au travers d'interfaces en Python ou R qui facilitent le développement expérimental. +Dans un contexte d'utilisation commerciale, les enjeux de performance deviennent rapidement significatifs compte tenu des volumes de données impliqués. Le souci qu'on y accorde au sein de la nouvelle génération d'algorithmes n'est sans doute pas étranger à ces impératifs commerciaux. Aussi, lorsqu'il est question de performance, le coeur d'un algorithme est typiquement développé dans un langage compilé (C/C++), bien que l'utilisateur interagit le plus souvent avec celui-ci au travers d'interfaces en Python ou R qui facilitent le développement expérimental. -Une facette intéressante du langage Julia est qu'il permet de briser cette barrière des 2 langages. La figure ci-dessous montre que contrairement à XGBoost, l'intégralité de l'implantation [Julia du gradient boosting](https://github.com/Evovest/EvoTrees.jl) est codée... en [Julia](https://julialang.org/)! +Une facette intéressante du langage Julia est qu'il permet de briser cette barrière des 2 langages. La figure ci-dessous montre que contrairement à XGBoost, l'intégralité de l'implantation [Julia du gradient boosting](https://github.com/Evovest/EvoTrees.jl) est codée... en [Julia](https://julialang.org/)! ![](xgboost_github.PNG) ![](julia_github.PNG) -On verra maintenant plus en détail comment il est possible d'implanter des algorithmes "from scratch" dans un langage convivial tout en obtenant des vitesses d'exécution compétitives avec les solutions les plus performantes sur le marché. +On verra maintenant plus en détail comment il est possible d'implanter des algorithmes "from scratch" dans un langage convivial tout en obtenant des vitesses d'exécution compétitives avec les solutions les plus performantes sur le marché. ## Mise en contexte -Afin de rendre plus tangibles les détails de l'implantation du gradient boosting en Julia, un problème de régression avec 2 variables continues servira d'exemple. +Afin de rendre plus tangibles les détails de l'implantation du gradient boosting en Julia, un problème de régression avec 2 variables continues servira d'exemple. La variable réponse est dépendante des variables `var1` et `var2`. L'effet est sinusoïdal en `var1` et croissant en `var2`. @@ -39,25 +39,24 @@ La variable réponse est dépendante des variables `var1` et `var2`. L'effet est ![](raw_one_ways.png) - ## Introduction à l'algorithme -L'entraînement d'un gradient boosting trees (GBT) peut être décrit sommairement de la manière suivante: +L'entraînement d'un gradient boosting trees (GBT) peut être décrit sommairement de la manière suivante: 0. Définir une prédiction de base pour chacune des observations. Ex: pred = 0.0 -1. Construire un arbre de décision, _A1_ expliquant la différence entre les prédictions et les valeurs observées. +1. Construire un arbre de décision, _A1_ expliquant la différence entre les prédictions et les valeurs observées. 2. Mettre à jour les prédictions en ajoutant les prédictions de l'arbre _A1_ aux prédictions actuelles: pred = pred + predict(_A1_) 3. Répéter 1. et 2. pour un nombre N d'arbres. -Dans un scénario où le nombre d'itérations serait de 4, le modèle entraîné pourrait se visualiser de la façon suivante: +Dans un scénario où le nombre d'itérations serait de 4, le modèle entraîné pourrait se visualiser de la façon suivante: ![](tree_group.png) -Pour obtenir une prédiction, il suffit d'additionner la prédiction obtenue à chacun des 4 arbres. +Pour obtenir une prédiction, il suffit d'additionner la prédiction obtenue à chacun des 4 arbres. -Comme on peut le constater, un modèle GBT consiste en une collection d'arbres de décision. Quelques subtilités sont néanmoins introduites en pratique, par exemple le rééchantillonnage des observations et des variables explicatives à chacune des itérations. Reste qu'une fois qu'on a établi comment construire un arbre de décision, l'essentiel du travail est accompli. À noter que la même logique s'appliquerait si on construisait un [RandomForest](https://fr.wikipedia.org/wiki/For%C3%AAt_d%27arbres_d%C3%A9cisionnels): il ne suffirait encore là que de savoir construire un arbre de décision, le reste n'étant qu'une variation de l'algorithme présenté plus haut. +Comme on peut le constater, un modèle GBT consiste en une collection d'arbres de décision. Quelques subtilités sont néanmoins introduites en pratique, par exemple le rééchantillonnage des observations et des variables explicatives à chacune des itérations. Reste qu'une fois qu'on a établi comment construire un arbre de décision, l'essentiel du travail est accompli. À noter que la même logique s'appliquerait si on construisait un [RandomForest](https://fr.wikipedia.org/wiki/For%C3%AAt_d%27arbres_d%C3%A9cisionnels): il ne suffirait encore là que de savoir construire un arbre de décision, le reste n'étant qu'une variation de l'algorithme présenté plus haut. -En Julia, on peut définir la structure du modèle de la manière suivante, où un GBTree est composé d'un vecteur de Tree: +En Julia, on peut définir la structure du modèle de la manière suivante, où un GBTree est composé d'un vecteur de Tree: ```julia struct GBTree @@ -67,15 +66,15 @@ struct GBTree end ``` -En son coeur Julia supporte des représentations multi-dimensionnelles via des `Array{T,N}`. Un vecteur `Vector{T}` ou une matrice `Matrix{T}` ne sont que des cas particuliers des `Array{T,N}`, où `N` = 1 et 2 respectivement. L'élément `T` réfère au type. Par exemple, un vecteur peut être défini par: `[1.1, 2.2]`. La nature de cet objet serait `Vector{Float64}`. En Julia, la représentation multi-dimensionnelle ne se limite pas aux nombres conventionnels comme les _Float_ ou les _Integer_, ça peut être n'importe quel type d’objet. Par exemple, on pourrait parfaitement avoir une matrice dont les éléments sont des _DataFrames_ (mais le produit matriciel de ces objets resterait à définir!). Dans le cas du GBT, le modèle est ainsi constitué d'un `Vector{Tree}`. +En son coeur Julia supporte des représentations multi-dimensionnelles via des `Array{T,N}`. Un vecteur `Vector{T}` ou une matrice `Matrix{T}` ne sont que des cas particuliers des `Array{T,N}`, où `N` = 1 et 2 respectivement. L'élément `T` réfère au type. Par exemple, un vecteur peut être défini par: `[1.1, 2.2]`. La nature de cet objet serait `Vector{Float64}`. En Julia, la représentation multi-dimensionnelle ne se limite pas aux nombres conventionnels comme les _Float_ ou les _Integer_, ça peut être n'importe quel type d’objet. Par exemple, on pourrait parfaitement avoir une matrice dont les éléments sont des _DataFrames_ (mais le produit matriciel de ces objets resterait à définir!). Dans le cas du GBT, le modèle est ainsi constitué d'un `Vector{Tree}`. ## Définition d'un arbre -Tel que montré plus haut, un arbre de décision se compose d'une série de noeuds comportant chacun une décision binaire. Par exemple, dans l'arbre ci-dessous, on commence par établir, pour chaque observation, si la variable 1 est plus petite que 0.996. Si oui, on va dans le segment de gauche et la décision suivante est si la variable 2 est plus petite que 1.12. On arrive ensuite à un noeud terminal qui indique la prédiction à associer à l'observation. +Tel que montré plus haut, un arbre de décision se compose d'une série de noeuds comportant chacun une décision binaire. Par exemple, dans l'arbre ci-dessous, on commence par établir, pour chaque observation, si la variable 1 est plus petite que 0.996. Si oui, on va dans le segment de gauche et la décision suivante est si la variable 2 est plus petite que 1.12. On arrive ensuite à un noeud terminal qui indique la prédiction à associer à l'observation. ![](tree_1.png) -Une structure récursive peut être une représentation intuitive pour un arbre qui serait alors défini comme un noeud contenant le critère de décision ainsi que 2 noeuds dépendants ("child nodes") selon que la condition soit respectée ou non. Il est également possible de représenter un arbre par un simple vecteur de noeuds: +Une structure récursive peut être une représentation intuitive pour un arbre qui serait alors défini comme un noeud contenant le critère de décision ainsi que 2 noeuds dépendants ("child nodes") selon que la condition soit respectée ou non. Il est également possible de représenter un arbre par un simple vecteur de noeuds: ```julia struct Tree{L, T<:AbstractFloat, S<:Int} @@ -94,17 +93,17 @@ struct TreeNode{L, T<:AbstractFloat, S<:Int, B<:Bool} end ``` -Comme on peut le voir dans la structure `TreeNode`, chaque noeud définit sur quelle variable la décision est prise (`feat`) ainsi que la condition à appliquer (`cond`). S'il s'agit d'un noeud terminal, il contiendra la prédiction (`pred`) et l'indicateur `split` sera à `false`. +Comme on peut le voir dans la structure `TreeNode`, chaque noeud définit sur quelle variable la décision est prise (`feat`) ainsi que la condition à appliquer (`cond`). S'il s'agit d'un noeud terminal, il contiendra la prédiction (`pred`) et l'indicateur `split` sera à `false`. -Une fois les structures établies, il ne reste plus qu'à identifier les valeurs qu'elles doivent prendre. +Une fois les structures établies, il ne reste plus qu'à identifier les valeurs qu'elles doivent prendre. ## Construction d'un arbre -Pour construire un arbre, il s'agit d'évaluer pour chaque variable la condition apportant la plus grande réduction de la fonction de perte (la somme des erreurs au carré par exemple). C'est là que l'essentiel de la charge de calcul se trouve et que certains choix de design permettront d'atteindre des performances optimales. Ensuite, la variable dont la condition optimale apporte le plus grand gain sera retenue pour définir la condition du noeud. +Pour construire un arbre, il s'agit d'évaluer pour chaque variable la condition apportant la plus grande réduction de la fonction de perte (la somme des erreurs au carré par exemple). C'est là que l'essentiel de la charge de calcul se trouve et que certains choix de design permettront d'atteindre des performances optimales. Ensuite, la variable dont la condition optimale apporte le plus grand gain sera retenue pour définir la condition du noeud. Pour chaque noeud, l'algorithme s'exerce d'une perspective univariée. Il s'agit là d'une propriété se prêtant à une optimisation. Puisque l'évaluation de la meilleure condition se fait de façon indépendante pour chaque variable, cette recherche peut aisément être parallélisée. -Julia supporte plusieurs saveurs de parallélisme. Dans le cas de la recherche de variables, tous les coeurs du processeur peuvent être mis à profit simplement en utilisant la macro `@threads` incluse dans les fonctionnalités de base du langage. +Julia supporte plusieurs saveurs de parallélisme. Dans le cas de la recherche de variables, tous les coeurs du processeur peuvent être mis à profit simplement en utilisant la macro `@threads` incluse dans les fonctionnalités de base du langage. ```julia @threads for j in cols @@ -112,11 +111,11 @@ Julia supporte plusieurs saveurs de parallélisme. Dans le cas de la recherche d end ``` -Une façon brute de chercher le meilleur bris est de mettre en ordre les observations selon une variable donnée. Une fois les observations en ordre, on peut considérer pour chacune des valeurs uniques prises par cette variable quel serait le gain si la condition s'exerçait sur cette valeur. +Une façon brute de chercher le meilleur bris est de mettre en ordre les observations selon une variable donnée. Une fois les observations en ordre, on peut considérer pour chacune des valeurs uniques prises par cette variable quel serait le gain si la condition s'exerçait sur cette valeur. -Une telle approche fonctionne, mais est sujette à quelques inconvénients. D'abord, ordonner une variable est une opération coûteuse, particulièrement si on considère que l'opération doit être répétée pour plusieurs variables, pour chacun des noeuds et pour chaque arbre. Également, si le nombre de valeurs uniques prises par une variable est très élevé, ça implique d'évaluer le gain à un très grand nombre de reprises. +Une telle approche fonctionne, mais est sujette à quelques inconvénients. D'abord, ordonner une variable est une opération coûteuse, particulièrement si on considère que l'opération doit être répétée pour plusieurs variables, pour chacun des noeuds et pour chaque arbre. Également, si le nombre de valeurs uniques prises par une variable est très élevé, ça implique d'évaluer le gain à un très grand nombre de reprises. -La méthode de l'histogramme permet de contourner ces obstacles. L'idée est de discrétiser chaque variable en associant chaque observation à un groupe, par exemple le quantile. En utilisant un entier entre 0 et 255 comme identifiant de ces groupes, la matrice de données est encodée dans un format `UInt8`, lequel accapare 8 fois moins de mémoire qu'un format `Float64` (un _numeric_ en R). +La méthode de l'histogramme permet de contourner ces obstacles. L'idée est de discrétiser chaque variable en associant chaque observation à un groupe, par exemple le quantile. En utilisant un entier entre 0 et 255 comme identifiant de ces groupes, la matrice de données est encodée dans un format `UInt8`, lequel accapare 8 fois moins de mémoire qu'un format `Float64` (un _numeric_ en R). Avant la construction des arbres, la librairie EvoTrees effectue cette discrétisation en trouvant d'abord les quantiles pour chacune des variables (`get_edges`), puis en créant une matrice de `UInt8` pour encoder les données d'entraînement. @@ -125,22 +124,22 @@ edges = EvoTrees.get_edges(X_train, params.nbins) X_bin = EvoTrees.binarize(X_train, edges) ``` -En choisissant le nombre de groupes (`nbins`) comme étant 16, le problème à résoudre prend la forme suivante d'un point de vue univarié: +En choisissant le nombre de groupes (`nbins`) comme étant 16, le problème à résoudre prend la forme suivante d'un point de vue univarié: ![](bin_one_ways.png) -Sous cette formulation, le nombre de conditions à évaluer se limite désormais à 15 (ou plus généralement, nbins-1). +Sous cette formulation, le nombre de conditions à évaluer se limite désormais à 15 (ou plus généralement, nbins-1). -Il reste enfin à définir le gain associé à chacun des bris. Une force du gradient boosting est sa flexibilité. Sous l'implantation introduite par XGboost, il suffit de définir une fonction de perte qui soit convexe. Par exemple, avec une régression des moindres carrés, la perte est définie par $(y - pred)^2$. Cette perte a une forme parabolique et son minimum est bien entendu lorsque la valeur prédite égale la valeur observée. La notion critique à remarquer est qu'en ne connaissant que les dérivées premières et secondes de la perte par rapport à la prédiction, il est possible de déterminer non seulement quelle serait la prédiction optimale, mais également le gain réalisé et ce, peu importe la fonction de perte grâce à une approximation de second degré: +Il reste enfin à définir le gain associé à chacun des bris. Une force du gradient boosting est sa flexibilité. Sous l'implantation introduite par XGboost, il suffit de définir une fonction de perte qui soit convexe. Par exemple, avec une régression des moindres carrés, la perte est définie par $(y - pred)^2$. Cette perte a une forme parabolique et son minimum est bien entendu lorsque la valeur prédite égale la valeur observée. La notion critique à remarquer est qu'en ne connaissant que les dérivées premières et secondes de la perte par rapport à la prédiction, il est possible de déterminer non seulement quelle serait la prédiction optimale, mais également le gain réalisé et ce, peu importe la fonction de perte grâce à une approximation de second degré: ```julia pred = -params.η .* node.∑δ ./ (node.∑δ² .+ params.λ .* node.∑𝑤) gain = sum((∑δ .^ 2 ./ (∑δ² .+ λ .* ∑𝑤)) ./ 2 ``` -Où η = vitesse d'apprentissage, ∑δ, ∑δ² = somme des dérivées premières et secondes, ∑w = somme des poids et λ = facteur de régularisation. +Où η = vitesse d'apprentissage, ∑δ, ∑δ² = somme des dérivées premières et secondes, ∑w = somme des poids et λ = facteur de régularisation. -Pour la première des 15 conditions possibles, l'arbre distinguerait les données en deux groupes (gauche et droite) de la manière suivante: +Pour la première des 15 conditions possibles, l'arbre distinguerait les données en deux groupes (gauche et droite) de la manière suivante: ![](first_split.png) @@ -148,7 +147,7 @@ Le gain se définit comme la réduction de la perte qu'apporterait une modificat ![](left_parabole.png) -La perte minimale est atteinte lorsque la prédiction est de -1.11, ce qui correspond à la moyenne des résidus pour le groupe 1. La perte sur l'ensemble des données est également calculée. Effectuer un bris dans l'arbre devra apporter un gain par rapport à cette valeur de référence. +La perte minimale est atteinte lorsque la prédiction est de -1.11, ce qui correspond à la moyenne des résidus pour le groupe 1. La perte sur l'ensemble des données est également calculée. Effectuer un bris dans l'arbre devra apporter un gain par rapport à cette valeur de référence. Au terme du processus d'entraînement, le modèle prend la forme suivante: @@ -156,22 +155,22 @@ Au terme du processus d'entraînement, le modèle prend la forme suivante: ## Évaluation de la performance -Afin d'évaluer si l'implantation de l'algorithme est compétitive, une [comparaison du temps d'entraînement](https://github.com/Evovest/EvoTrees.jl/blob/master/blog/benchmarks.jl) par rapport à XGBoost pour 100 itérations sur des données générées aléatoirement est conduite: +Afin d'évaluer si l'implantation de l'algorithme est compétitive, une [comparaison du temps d'entraînement](https://github.com/Evovest/EvoTrees.jl/blob/master/blog/benchmarks.jl) par rapport à XGBoost pour 100 itérations sur des données générées aléatoirement est conduite: | Dimensions / Algo | XGBoost Exact | XGBoost Hist | EvoTrees | -|-------------------|:-------------:|:------------:|:--------:| -| 10K x 100 | 1.18s | 2.15s | 0.52s | -| 100K x 100 | 9.39s | 4.25s | 2.02s | -| 1M X 100 | 146.5s | 20.2s | 21.5s | +| ----------------- | :-----------: | :----------: | :------: | +| 10K x 100 | 1.18s | 2.15s | 0.52s | +| 100K x 100 | 9.39s | 4.25s | 2.02s | +| 1M X 100 | 146.5s | 20.2s | 21.5s | -Il en ressort que la méthode par histogramme est critique pour obtenir de bonnes performances sur des données volumineuses. Également, EvoTrees se compare avantageusement à XGBoost sur des données de petite taille et affiche des performances comparables sur des données plus volumineuses. +Il en ressort que la méthode par histogramme est critique pour obtenir de bonnes performances sur des données volumineuses. Également, EvoTrees se compare avantageusement à XGBoost sur des données de petite taille et affiche des performances comparables sur des données plus volumineuses. -EvoTrees supporte par ailleurs quelques fonctions de pertes qu'on ne retrouve pas dans XGBoost, dont la régression par quantile ainsi que la régression gaussienne (estimation simultanée des paramètres $\mu$ et $\sigma$ de la distribution). +EvoTrees supporte par ailleurs quelques fonctions de pertes qu'on ne retrouve pas dans XGBoost, dont la régression par quantile ainsi que la régression gaussienne (estimation simultanée des paramètres $\mu$ et $\sigma$ de la distribution). ## Développements futurs -Une piste de développement serait de considérer différents modes de parallélisme avec des données plus volumineuses afin de combler l'écart de performance lorsque les observations sont > 1 M, par exemple en parallélisant la construction d'histogrammes à l'intérieur d'une même variable. Également, fidèle à l'esprit de résolution du problème des deux langages, Julia offre des fonctionnalités prometteuses pour le développement d'algorithmes sur [GPU](https://juliacomputing.com/domains/gpus.html). Supporter la construction d'histogrammes en CUDA pourrait ainsi être la meilleure réponse pour le traitement de données très volumineuses. +Une piste de développement serait de considérer différents modes de parallélisme avec des données plus volumineuses afin de combler l'écart de performance lorsque les observations sont > 1 M, par exemple en parallélisant la construction d'histogrammes à l'intérieur d'une même variable. Également, fidèle à l'esprit de résolution du problème des deux langages, Julia offre des fonctionnalités prometteuses pour le développement d'algorithmes sur [GPU](https://juliacomputing.com/domains/gpus.html). Supporter la construction d'histogrammes en CUDA pourrait ainsi être la meilleure réponse pour le traitement de données très volumineuses. ## Conclusion -Au-delà de la librairie EvoTrees, Julia offre un environnement de choix pour toute tâche exigeante en calculs. Le projet [MLJ](https://github.com/alan-turing-institute/MLJ.jl) auquel Evotrees est intégré offre un bon point d'entrée pour tout projet de modélisation traditionnel. Plus significatif encore est de réaliser que de nombreuses routines algorithmiques peuvent être implantées relativement aisément, permettant de se libérer des lourdeurs communes aux librairies clés en main pour se concentrer sur les caractéristiques uniques du problème à résoudre. La légèreté de l'outil d'apprentissage profond [Flux](https://fluxml.ai/) en est un exemple éloquent. +Au-delà de la librairie EvoTrees, Julia offre un environnement de choix pour toute tâche exigeante en calculs. Le projet [MLJ](https://github.com/alan-turing-institute/MLJ.jl) auquel Evotrees est intégré offre un bon point d'entrée pour tout projet de modélisation traditionnel. Plus significatif encore est de réaliser que de nombreuses routines algorithmiques peuvent être implantées relativement aisément, permettant de se libérer des lourdeurs communes aux librairies clés en main pour se concentrer sur les caractéristiques uniques du problème à résoudre. La légèreté de l'outil d'apprentissage profond [Flux](https://fluxml.ai/) en est un exemple éloquent. diff --git a/content/blog/2020-03-04-what-is-wrong-with-sklearn/index.en.md b/content/blog/2020-03-04-what-is-wrong-with-sklearn/index.en.md index 9fd6ed0a0..56762fe0f 100644 --- a/content/blog/2020-03-04-what-is-wrong-with-sklearn/index.en.md +++ b/content/blog/2020-03-04-what-is-wrong-with-sklearn/index.en.md @@ -1,15 +1,16 @@ --- -title: "What's wrong with Scikit-Learn." -author: "Guillaume Chevalier" +title: "What's wrong with Scikit-Learn." +author: 'Guillaume Chevalier' date: '2020-01-03' slug: neat-machine-learning-pipelines type: post canonical: https://www.neuraxio.com/en/blog/scikit-learn/2020/01/03/what-is-wrong-with-scikit-learn.html -categories: ["Software engineering", "Python"] -tags: ["Scikit Learn", "Machine Learning", "Deep Learning", "Clean Code", "Software Architecture"] -description: "Scikit-Learn had its first release in 2007, which was a pre deep learning era. However, it’s one of the most known and adopted machine learning library, and is still growing." -featured: "sklearn-broken.jpg" -featuredpath: "img/headers/" +categories: ['Software engineering', 'Python'] +tags: ['Scikit Learn', 'Machine Learning', 'Deep Learning', 'Clean Code', 'Software Architecture'] +description: 'Scikit-Learn had its first release in 2007, which was a pre deep learning era. However, it’s one of the most known and adopted machine learning library, and is still growing.' +featured: 'sklearn-broken.jpg' +featuredpath: 'img/headers/' +aliases: [/blog/2020-03-04-what-is-wrong-with-sklearn/neat-machine-learning-pipelines/] --- > Scikit-Learn’s “pipe and filter” design pattern is simply beautiful. But how to use it for Deep Learning, AutoML, and complex production-level pipelines? @@ -100,21 +101,21 @@ Metaestimators are crucial for advanced features. For instance, a `ParallelTrans For sure, Scikit-Learn is very convenient and well-built. However, it needs a refresh. Here are our solutions with Neuraxle to make Scikit-Learn fresh and useable within modern computing projects! -- [Inability to Reasonably do Automatic Machine Learning (AutoML)](https://www.neuraxle.org/stable/scikit-learn_problems_solutions.html#inability-to-reasonably-do-automatic-machine-learning-automl) - - [Problem: Defining the Search Space (Hyperparameter Distributions)](https://www.neuraxle.org/stable/scikit-learn_problems_solutions.html#problem-defining-the-search-space-hyperparameter-distributions) - - [Problem: Defining Hyperparameters in the Constructor is Limiting](https://www.neuraxle.org/stable/scikit-learn_problems_solutions.html#problem-defining-hyperparameters-in-the-constructor-is-limiting) - - [Problem: Different Train and Test Behavior](https://www.neuraxle.org/stable/scikit-learn_problems_solutions.html#problem-different-train-and-test-behavior) - - [Problem: You trained a Pipeline and You Want Feedback on its Learning.](https://www.neuraxle.org/stable/scikit-learn_problems_solutions.html#problem-you-trained-a-pipeline-and-you-want-feedback-statistics-on-its-learning) +- [Inability to Reasonably do Automatic Machine Learning (AutoML)](https://www.neuraxle.org/stable/scikit-learn_problems_solutions.html#inability-to-reasonably-do-automatic-machine-learning-automl) + - [Problem: Defining the Search Space (Hyperparameter Distributions)](https://www.neuraxle.org/stable/scikit-learn_problems_solutions.html#problem-defining-the-search-space-hyperparameter-distributions) + - [Problem: Defining Hyperparameters in the Constructor is Limiting](https://www.neuraxle.org/stable/scikit-learn_problems_solutions.html#problem-defining-hyperparameters-in-the-constructor-is-limiting) + - [Problem: Different Train and Test Behavior](https://www.neuraxle.org/stable/scikit-learn_problems_solutions.html#problem-different-train-and-test-behavior) + - [Problem: You trained a Pipeline and You Want Feedback on its Learning.](https://www.neuraxle.org/stable/scikit-learn_problems_solutions.html#problem-you-trained-a-pipeline-and-you-want-feedback-statistics-on-its-learning) - [Inability to Reasonably do Deep Learning Pipelines](https://www.neuraxle.org/stable/scikit-learn_problems_solutions.html#inability-to-reasonably-do-deep-learning-pipelines) - - [Problem: Scikit-Learn Hardly Allows for Mini-Batch Gradient Descent (Incremental Fit)](https://www.neuraxle.org/stable/scikit-learn_problems_solutions.html#problem-scikit-learn-hardly-allows-for-mini-batch-gradient-descent-incremental-fit) - - [Problem: Initializing the Pipeline and Deallocating Resources](https://www.neuraxle.org/stable/scikit-learn_problems_solutions.html#problem-initializing-the-pipeline-and-deallocating-resources) - - [Problem: It is Difficult to Use Other Deep Learning (DL) Libraries in Scikit-Learn](https://www.neuraxle.org/stable/scikit-learn_problems_solutions.html#problem-it-is-difficult-to-use-other-deep-learning-dl-libraries-in-scikit-learn) - - [Problem: The Ability to Transform Output Labels](https://www.neuraxle.org/stable/scikit-learn_problems_solutions.html#problem-the-ability-to-transform-output-labels) + - [Problem: Scikit-Learn Hardly Allows for Mini-Batch Gradient Descent (Incremental Fit)](https://www.neuraxle.org/stable/scikit-learn_problems_solutions.html#problem-scikit-learn-hardly-allows-for-mini-batch-gradient-descent-incremental-fit) + - [Problem: Initializing the Pipeline and Deallocating Resources](https://www.neuraxle.org/stable/scikit-learn_problems_solutions.html#problem-initializing-the-pipeline-and-deallocating-resources) + - [Problem: It is Difficult to Use Other Deep Learning (DL) Libraries in Scikit-Learn](https://www.neuraxle.org/stable/scikit-learn_problems_solutions.html#problem-it-is-difficult-to-use-other-deep-learning-dl-libraries-in-scikit-learn) + - [Problem: The Ability to Transform Output Labels](https://www.neuraxle.org/stable/scikit-learn_problems_solutions.html#problem-the-ability-to-transform-output-labels) - [Not ready for Production nor for Complex Pipelines](https://www.neuraxle.org/stable/scikit-learn_problems_solutions.html#not-ready-for-production-nor-for-complex-pipelines) - - [Problem: Processing 3D, 4D, or ND Data in your Pipeline with Steps Made for Lower-Dimensionnal Data](https://www.neuraxle.org/stable/scikit-learn_problems_solutions.html#problem-processing-3d-4d-or-nd-data-in-your-pipeline-with-steps-made-for-lower-dimensionnal-data) - - [Problem: Modify a Pipeline Along the Way, such as for Pre-Training or Fine-Tuning](https://www.neuraxle.org/stable/scikit-learn_problems_solutions.html#problem-modify-a-pipeline-along-the-way-such-as-for-pre-training-or-fine-tuning) - - [Problem: Getting Model Attributes from Scikit-Learn Pipeline](https://www.neuraxle.org/stable/scikit-learn_problems_solutions.html#problem-getting-model-attributes-from-scikit-learn-pipeline) - - [Problem: You can't Parallelize nor Save Pipelines Using Steps that Can't be Serialized "as-is" by Joblib](https://www.neuraxle.org/stable/scikit-learn_problems_solutions.html#problem-you-can-t-parallelize-nor-save-pipelines-using-steps-that-can-t-be-serialized-as-is-by-joblib) + - [Problem: Processing 3D, 4D, or ND Data in your Pipeline with Steps Made for Lower-Dimensionnal Data](https://www.neuraxle.org/stable/scikit-learn_problems_solutions.html#problem-processing-3d-4d-or-nd-data-in-your-pipeline-with-steps-made-for-lower-dimensionnal-data) + - [Problem: Modify a Pipeline Along the Way, such as for Pre-Training or Fine-Tuning](https://www.neuraxle.org/stable/scikit-learn_problems_solutions.html#problem-modify-a-pipeline-along-the-way-such-as-for-pre-training-or-fine-tuning) + - [Problem: Getting Model Attributes from Scikit-Learn Pipeline](https://www.neuraxle.org/stable/scikit-learn_problems_solutions.html#problem-getting-model-attributes-from-scikit-learn-pipeline) + - [Problem: You can't Parallelize nor Save Pipelines Using Steps that Can't be Serialized "as-is" by Joblib](https://www.neuraxle.org/stable/scikit-learn_problems_solutions.html#problem-you-can-t-parallelize-nor-save-pipelines-using-steps-that-can-t-be-serialized-as-is-by-joblib) ## Conclusion diff --git a/content/blog/2020-03-19-howto/index.en.md b/content/blog/2020-03-19-howto/index.en.md index 842271e56..2c2b7094c 100644 --- a/content/blog/2020-03-19-howto/index.en.md +++ b/content/blog/2020-03-19-howto/index.en.md @@ -4,35 +4,35 @@ author: Samuel Perreault and David Beauchemin date: '2020-04-21' slug: howto-en type: post -categories: ["Contribute"] +categories: ['Contribute'] tags: [] -description: "A template to contribute to the blog .Layer" -featured: "howto-cover.png" -featuredpath: "img/headers/" -reading_time: "" +description: 'A template to contribute to the blog .Layer' +featured: 'howto-cover.png' +featuredpath: 'img/headers/' +reading_time: '' +aliases: [/blog/2020-03-19-howto/howto-en/] --- -Contributing to the blog has never been easier. First of all, it must be said that any submission, whatever its format (Markdown, Microsoft Word, Notepad, *name it*!), will be considered, and ultimately transcribed into Markdown by our team. We offer the option to submit an article [here](https://dotlayer.org/en/contribute), and we are already thinking of a way to review non-Markdown documents (possibly Google Docs). This being written, for those who would like to write and submit a post in the _conventional_ way, here is a simple procedure to get there. +Contributing to the blog has never been easier. First of all, it must be said that any submission, whatever its format (Markdown, Microsoft Word, Notepad, _name it_!), will be considered, and ultimately transcribed into Markdown by our team. We offer the option to submit an article [here](https://dotlayer.org/en/contribute), and we are already thinking of a way to review non-Markdown documents (possibly Google Docs). This being written, for those who would like to write and submit a post in the _conventional_ way, here is a simple procedure to get there. 1. **Blog post creation** - 1. Save the `.md` file used to create this post (available [here](https://github.com/dot-layer/blog/blob/master/content/blog/2020-03-19-howto/index.en.md)) under the name `index.en.md` (for posts in English) or` index.fr.md` (for posts in French). - 2. Insert your post and modify the header's essential fields as well as the content (duh). You must save your images in the same folder as the .md file (or in a sub-directory); except for the cover image, its location is specified with the `featured:" "` field. + 1. Save the `.md` file used to create this post (available [here](https://github.com/dot-layer/blog/blob/master/content/blog/2020-03-19-howto/index.en.md)) under the name `index.en.md` (for posts in English) or` index.fr.md` (for posts in French). + 2. Insert your post and modify the header's essential fields as well as the content (duh). You must save your images in the same folder as the .md file (or in a sub-directory); except for the cover image, its location is specified with the `featured:" "` field. 2. **Submission of the blog post** - 1. `git fork https://github.com/dot-layer/blog` directory, i.e. *fork* the [blog directory](https://github.com/dot-layer/blog). - 2. `git checkout -b post/your-initials_post-name`, i.e. create a new branch for your post. - 3. Create a new directory `content/ blog/YYYY-mm-dd-post-name` and insert your post (.md or .html) as well as the static files (e.g. images) necessary for its compilation. - 4. Make a *Pull Request* to the branch *master* on the [blog github](https://github.com/dot-layer/blog/pulls). + 1. `git fork https://github.com/dot-layer/blog` directory, i.e. _fork_ the [blog directory](https://github.com/dot-layer/blog). + 2. `git checkout -b post/your-initials_post-name`, i.e. create a new branch for your post. + 3. Create a new directory `content/ blog/YYYY-mm-dd-post-name` and insert your post (.md or .html) as well as the static files (e.g. images) necessary for its compilation. + 4. Make a _Pull Request_ to the branch _master_ on the [blog github](https://github.com/dot-layer/blog/pulls). Let's take a closer look at each of the steps. But first, a few comments of interest. ## License and reproducibility -All blog posts are subject to the license [CC-BY](https://creativecommons.org/licenses/by/4.0/deed). Also, if you want to publish an article already published on another platform on the .Layer blog, please mention it in the post (at the end), as well as in the *Pull Request*. Finally, keep in mind that the main objective of the blog is education and knowledge sharing. +All blog posts are subject to the license [CC-BY](https://creativecommons.org/licenses/by/4.0/deed). Also, if you want to publish an article already published on another platform on the .Layer blog, please mention it in the post (at the end), as well as in the _Pull Request_. Finally, keep in mind that the main objective of the blog is education and knowledge sharing. For reproducibility over time and for all, **the repository must remain independent of any code compilation (R, Python, Julia, etc.)**. For example, if you use RMarkdown (.Rmd), which allows you to integrate R code in a Markdown file, you will have to compile everything and copy only .md or .html in the blog repository (at step 2.3). - # 1. Blog post creation **Step 1.1.** Save, under the name `index.en.md` /` index.fr.md` (English / French), the .md file [source](https://github.com/dot-layer/blog/blob/master/content/blog/2020-03-19-howto/index.en.md) used to create this blog post. This step is simply to save you from copying the header (in [YAML] format(https://docs.ansible.com/ansible/latest/reference_appendices/YAMLSyntax.html)), which contains some essential fields to fill in. @@ -54,20 +54,22 @@ featuredpath: "img/headers/" reading_time: "" --- ``` -The fields are almost all *self-explanatory*. -The `date` field should contain the creation date of the file. We will change it for the publication date in due time (in the *Pull Request*). + +The fields are almost all _self-explanatory_. +The `date` field should contain the creation date of the file. We will change it for the publication date in due time (in the _Pull Request_). We will also take care of the `categories` and` tags` fields. -The `slug` field is a *nickname* for your post, which will be used to name the various files linked to the post in the blog directory. +The `slug` field is a _nickname_ for your post, which will be used to name the various files linked to the post in the blog directory. The `featured` field should contain the name of the cover image file, while` featuredpath` (which should remain unchanged) indicates where to find the file. This is where you should place your cover image. -Finally, if the post submitted is already published on another platform, please add the `canonical` field to specify the platform. For example, in the header of the post *What's wrong with Scikit-Learn* published on the blog, we find +Finally, if the post submitted is already published on another platform, please add the `canonical` field to specify the platform. For example, in the header of the post _What's wrong with Scikit-Learn_ published on the blog, we find + ``` canonical: https://www.neuraxio.com/en/blog/scikit-learn/2020/01/03/what-is-wrong-with-scikit-learn.html ``` -in addition to the mention at the end of the post, which refers to the original publication. +in addition to the mention at the end of the post, which refers to the original publication. As for writing the post, you have to know the basics of Markdown. -In addition to the content already in the *template* that constitutes this post, we recommend this little [*cheatsheet*] (https://github.com/adam-p/markdown-here/wiki/Markdown-Here-Cheatsheet) to use Markdown. +In addition to the content already in the _template_ that constitutes this post, we recommend this little [*cheatsheet*] (https://github.com/adam-p/markdown-here/wiki/Markdown-Here-Cheatsheet) to use Markdown. # 2. Submission of a blog post @@ -75,7 +77,6 @@ In addition to the content already in the *template* that constitutes this post, **Steps 2.3.** Create a new directory/folder `content/blog/YYYY-mm-dd-post-name` (in your new repository created in the previous step) and insert your post and the static files (e.g. images) necessary for its compilation. As mentioned earlier in the post, we ask to stick to the .md (or .html), it is no longer a question of compiling R code, Python, Julia, etc. when the post is included in the blog repository. -**Step 2.4.** Make a *Pull Request* to the *master* branch on the [blog github](https://github.com/dot-layer/blog). Another classic Git operation. From there, contributors who manage the blog will review the post, make constructive recommendations, and ensure that the rendering is *clean*. - +**Step 2.4.** Make a _Pull Request_ to the _master_ branch on the [blog github](https://github.com/dot-layer/blog). Another classic Git operation. From there, contributors who manage the blog will review the post, make constructive recommendations, and ensure that the rendering is _clean_. So there ... No more complicated than that. We hope this will help you. diff --git a/content/blog/2020-03-19-howto/index.fr.md b/content/blog/2020-03-19-howto/index.fr.md index d57c5c848..81c6ce34a 100644 --- a/content/blog/2020-03-19-howto/index.fr.md +++ b/content/blog/2020-03-19-howto/index.fr.md @@ -4,35 +4,35 @@ author: Samuel Perreault et David Beauchemin date: '2020-04-21' slug: howto-fr type: post -categories: ["Contribuer"] +categories: ['Contribuer'] tags: [] -description: "Un gabarit pour contribuer au blog .Layer" -featured: "howto-cover.png" -featuredpath: "img/headers/" -reading_time: "" +description: 'Un gabarit pour contribuer au blog .Layer' +featured: 'howto-cover.png' +featuredpath: 'img/headers/' +reading_time: '' +aliases: [/blog/2020-03-19-howto/howto-fr/] --- -Contribuer au blog n'aura jamais été aussi facile. Tout d'abord, il faut dire que toute soumission, quel que soit son format (Markdown, Microsoft Word, Notepad, *name it*!), sera considérée, et ultimement transcrite en Markdown. On offre l'option de soumettre un article [ici](https://dotlayer.org/contribute) et on pense déjà à une façon de faire pour la révision des documents non-Markdown (possiblement Google Docs). Ceci étant écrit, pour ceux et celles qui voudraient écrire et soumettre un article de la façon _conventionnelle_, voici une procédure simple pour y arriver. +Contribuer au blog n'aura jamais été aussi facile. Tout d'abord, il faut dire que toute soumission, quel que soit son format (Markdown, Microsoft Word, Notepad, _name it_!), sera considérée, et ultimement transcrite en Markdown. On offre l'option de soumettre un article [ici](https://dotlayer.org/contribute) et on pense déjà à une façon de faire pour la révision des documents non-Markdown (possiblement Google Docs). Ceci étant écrit, pour ceux et celles qui voudraient écrire et soumettre un article de la façon _conventionnelle_, voici une procédure simple pour y arriver. 1. **Création de l'article** - 1. Enregister le fichier `.md` utilisé pour créer cet article (disponible [ici](https://github.com/dot-layer/blog/blob/master/content/blog/2020-03-19-howto/index.fr.md)) sous le nom `index.en.md` (pour les articles en anglais) ou `index.fr.md` (pour les articles en français). - 2. Y insérer votre article et modifier les champs essentiels de l'en-tête ainsi que le contenu (duh). Il faut sauvegarder vos images dans le même dossier que le fichier .md (ou dans un sous-répertoire); à l'exception de l'image de couverture, son emplacement est spécifié avec le champ `featured: ""`. + 1. Enregister le fichier `.md` utilisé pour créer cet article (disponible [ici](https://github.com/dot-layer/blog/blob/master/content/blog/2020-03-19-howto/index.fr.md)) sous le nom `index.en.md` (pour les articles en anglais) ou `index.fr.md` (pour les articles en français). + 2. Y insérer votre article et modifier les champs essentiels de l'en-tête ainsi que le contenu (duh). Il faut sauvegarder vos images dans le même dossier que le fichier .md (ou dans un sous-répertoire); à l'exception de l'image de couverture, son emplacement est spécifié avec le champ `featured: ""`. 2. **Soumission de l'article** - 1. `git fork https://github.com/dot-layer/blog` le répertoire, c'est-à-dire *fourcher* le [répertoire du blog](https://github.com/dot-layer/blog). - 2. `git checkout -b post/tes-initiales_nom-du-post`, c'est-à-dire créer une nouvelle branche pour votre article. - 3. Créer un nouveau répertoire `content/blog/YYYY-mm-dd-nom-du-post` et y insérer votre article (.md ou .html) ainsi que les fichiers statiques (e.g. images) nécessaires à sa compilation. - 4. Faire un *Pull Request* à la branche *master* sur le [github du blog](https://github.com/dot-layer/blog/pulls). + 1. `git fork https://github.com/dot-layer/blog` le répertoire, c'est-à-dire _fourcher_ le [répertoire du blog](https://github.com/dot-layer/blog). + 2. `git checkout -b post/tes-initiales_nom-du-post`, c'est-à-dire créer une nouvelle branche pour votre article. + 3. Créer un nouveau répertoire `content/blog/YYYY-mm-dd-nom-du-post` et y insérer votre article (.md ou .html) ainsi que les fichiers statiques (e.g. images) nécessaires à sa compilation. + 4. Faire un _Pull Request_ à la branche _master_ sur le [github du blog](https://github.com/dot-layer/blog/pulls). Voyons en détail chacune des étapes. Mais tout d'abord, quelques commentaires d'intérêts. ## Licence et reproductibilité -Tous les articles sur le blog sont assujettis à la license [CC-BY](https://creativecommons.org/licenses/by/4.0/deed.fr). Aussi, si vous souhaitez publier sur le blog de .Layer un article déjà paru sur une autre plateforme, veuillez s'il vous plaît le mentionner dans l'article (à la fin), ainsi que dans le *Pull Request*. Finalement, gardez en tête que l'objectif principal du blog est l'éducation et le partage du savoir. +Tous les articles sur le blog sont assujettis à la license [CC-BY](https://creativecommons.org/licenses/by/4.0/deed.fr). Aussi, si vous souhaitez publier sur le blog de .Layer un article déjà paru sur une autre plateforme, veuillez s'il vous plaît le mentionner dans l'article (à la fin), ainsi que dans le _Pull Request_. Finalement, gardez en tête que l'objectif principal du blog est l'éducation et le partage du savoir. À des fins de reproductibilité dans le temps et pour tous, **le dépôt doit rester indépendant de toute compilation de code (R, Python, Julia, etc.)**. Par exemple, si vous utilisez RMarkdown (.Rmd), qui permet d'intégrer du code R dans un fichier Markdown, vous devrez alors compiler le tout et copier seulement le .md ou le .html dans le dépôt du blog (à l'étape 2.3). - # 1. Création d'un article **Étape 1.1.** Enregister, sous le nom `index.en.md`/`index.fr.md` (anglais/français), le fichier .md [source](https://github.com/dot-layer/blog/blob/master/content/blog/2020-03-19-howto/index.fr.md) utilisé pour créer le présent article. Cette étape sert simplement à vous éviter de copier l'en-tête (en format [YAML](https://docs.ansible.com/ansible/latest/reference_appendices/YAMLSyntax.html)), qui contient quelques champs essentiels à remplir. @@ -54,20 +54,22 @@ featuredpath: "img/headers/" reading_time: "" --- ``` -Les champs sont presque tous *self-explanatory*. -Le champ `date` devrait contenir la date de création du fichier. On le changera pour la date de publication en temps et lieu (dans le *Pull Request*). + +Les champs sont presque tous _self-explanatory_. +Le champ `date` devrait contenir la date de création du fichier. On le changera pour la date de publication en temps et lieu (dans le _Pull Request_). On s'occupera aussi des champs `categories` et `tags`. -Le champ `slug` est un *surnom* pour votre article qui sera utilisé pour nommer les différents dossiers liés à l'article sur le répertoire du blog. +Le champ `slug` est un _surnom_ pour votre article qui sera utilisé pour nommer les différents dossiers liés à l'article sur le répertoire du blog. Le champ `featured` doit contenir le nom du fichier de l'image de couverture, tandis que `featuredpath` (qui doit rester inchangé) indique où trouver le fichier. C'est d'ailleurs là que vous devez placer votre image de couverture. -Finalement, si l'article soumis est déjà publié sur une autre plateforme, veuillez ajouter le champ `canonical` afin de spécifier ladite plateforme. Par exemple, dans l'en-tête de l'article *What's wrong with Scikit-Learn* publié sur le blog, on trouve +Finalement, si l'article soumis est déjà publié sur une autre plateforme, veuillez ajouter le champ `canonical` afin de spécifier ladite plateforme. Par exemple, dans l'en-tête de l'article _What's wrong with Scikit-Learn_ publié sur le blog, on trouve + ``` canonical: https://www.neuraxio.com/en/blog/scikit-learn/2020/01/03/what-is-wrong-with-scikit-learn.html ``` -en plus de la mention à la fin de l'article qui réfère à la publication originale. +en plus de la mention à la fin de l'article qui réfère à la publication originale. -En ce qui a trait à l'écriture de l'article, il faut connaître les bases de Markdown. -En plus du contenu déjà dans le *template* que constitue cet article, on vous conseille cette petite [*cheatsheet*](https://github.com/adam-p/markdown-here/wiki/Markdown-Here-Cheatsheet) pour utiliser Markdown. +En ce qui a trait à l'écriture de l'article, il faut connaître les bases de Markdown. +En plus du contenu déjà dans le _template_ que constitue cet article, on vous conseille cette petite [_cheatsheet_](https://github.com/adam-p/markdown-here/wiki/Markdown-Here-Cheatsheet) pour utiliser Markdown. # 2. Soumission d'un article @@ -75,7 +77,6 @@ En plus du contenu déjà dans le *template* que constitue cet article, on vous **Étapes 2.3.** Créer un nouveau répertoire/dossier `content/blog/YYYY-mm-dd-nom-du-post` (dans votre nouveau dépôt créé à l'étape précédente) et y insérer votre article ainsi que les fichiers statiques (e.g. images) nécessaires à sa compilation. Comme mentionné plus tôt dans l'article, on demande de s'en tenir au .md (ou .html), il n'est plus question de compiler de code R, Python, Julia, etc. lorsque l'article est intégré au dépôt du blog. -**Étape 2.4.** Faire un *Pull Request* à la branche *master* sur le [github du blog](https://github.com/dot-layer/blog). Encore une opération classique de Git. À partir de là, les collaborateurs qui gèrent le blogue feront une révision de l'article et des recommandations constructives, en plus de s'assurer que le rendu est *clean*. - +**Étape 2.4.** Faire un _Pull Request_ à la branche _master_ sur le [github du blog](https://github.com/dot-layer/blog). Encore une opération classique de Git. À partir de là, les collaborateurs qui gèrent le blogue feront une révision de l'article et des recommandations constructives, en plus de s'assurer que le rendu est _clean_. Euh voilà... Pas plus compliqué que ça. En espérant que ça vous aidera. diff --git a/content/blog/2020-06-09/index.fr.md b/content/blog/2020-06-09/index.fr.md index dd2c9c8df..5c621af2b 100644 --- a/content/blog/2020-06-09/index.fr.md +++ b/content/blog/2020-06-09/index.fr.md @@ -4,11 +4,12 @@ author: Nicolas Garneau date: '2020-07-29' slug: copenhagen type: post -categories: ["Trippin"] +categories: ['Trippin'] tags: [] -description: "Étendre ses frontières en recherche" -featured: "copenhagen-fr-cover.jpg" -featuredpath: "img/headers/" +description: 'Étendre ses frontières en recherche' +featured: 'copenhagen-fr-cover.jpg' +featuredpath: 'img/headers/' +aliases: ['/blog/2020-06-09/copenhagen/'] --- L’hiver dernier, j’ai eu la chance d’aller à Copenhague, au Danemark, dans le cadre de ma recherche de doctorat. @@ -23,7 +24,7 @@ Clairement, le gars est tombé sur la tête; ça ne fait pas partie du chemineme Il y a en fait trois principales raisons. -La première, très simple, est que j'adore voyager. +La première, très simple, est que j'adore voyager. La deuxième est que je voulais tout simplement rencontrer des gens, élargir mon réseau de contacts. La troisième est que je crois qu'il est fondamental pour un chercheur d'étendre ses horizons en recherche. @@ -39,6 +40,7 @@ Sebastian Ruder en glisse un mot dans son excellent billet ["10 Tips for Researc Il y a évidemment quelques préparatifs nécessaires pour concrétiser un voyage de recherche. Ça se résume essentiellement en 3 étapes; + - Trouver un endroit où faire votre stage; - Avoir/obtenir du financement; - Trouver votre hébergement. @@ -49,7 +51,6 @@ D'où ma décision de partir seulement 3 mois. Je détaille donc les 3 étapes selon mon expérience personnelle. - ### Trouver un laboratoire En 2014, j'ai fait une session d'étude à Toulouse en France et le bureau international de l'université Laval nous donne un sacré coup de main pour l'organisation du projet. @@ -69,18 +70,17 @@ Ce n'est rien pour critiquer les autres chercheurs, ils sont tous très occupés Cependant, Anders était extrêmement réactif considérant le fait que j'étais un parfait inconnu. Ça confirmait donc mon stage de 3 mois du 4 février au 27 avril 2020 à Copenhague! - ### Le financement Si vous avez votre propre "stash" de côté, c'est l'idéal. -J'avais quelques dollars en banque, mais Copenhague est une des villes les plus chères *à visiter*. +J'avais quelques dollars en banque, mais Copenhague est une des villes les plus chères _à visiter_. Et si comme moi vous aimez la bonne bouffe, la boisson et les produits locaux, ça chiffre assez vite. Comme mentionné précédemment, les universités sont souvent munies d'un bureau international. Celui de l'université Laval fournit une bourse de 1 000\\$ pour tout stage à l'étranger aux cycles supérieurs, ce qui couvrait essentiellement mon billet d'avion de 1 023\\$ réservé en octobre pour un départ en février. Les associations étudiantes peuvent également vous donner un coup de main. -[L'AELIÉS](https://www.aelies.ulaval.ca/services/soutien-financier/), l'association des étudiant(e)s inscrits aux études supérieures, offre également un soutien de 300$ pour les stages ou voyages d'études. +[L'AELIÉS](https://www.aelies.ulaval.ca/services/soutien-financier/), l'association des étudiant(e)s inscrits aux études supérieures, offre également un soutien de 300$ pour les stages ou voyages d'études. Ce n’est pas énorme, mais très rapide de faire la demande et c'est mieux qu'une claque en plein visage. Au niveau gouvernemental, il y a (avait) quelques possibilités. @@ -92,7 +92,6 @@ Ce qui a beaucoup de sens en effet. Bref, je me retrouvais donc avec une bourse de 1 300\\$ pour partir 3 mois à Copenhague :( - ### L'hébergement Pour ce qui est de l'hébergement, j'ai choisi d'opter pour la formule économique; une chambre dans un Airbnb. @@ -107,7 +106,7 @@ Copenhague est sans aucun doute une des plus belles villes en Europe que j'ai vi Très propre, on y circule facilement à vélo (il faut absolument s'en procurer un, [Swapfiets](https://swapfiets.dk/en/) est parfait pour ça), la nourriture excellente et d'impressionnantes microbrasseries. On ajoute à tout ça la chaleur des gens. Coastal est un laboratoire d'une quinzaine de chercheurs (qui sont régulièrement présents) et chacun d'entre eux m'a personnellement accueilli. -Le groupe organise régulièrement des activités, *after work beer* et participe religieusement aux *pub quiz* du [Mikkeller](https://mikkeller.com/). +Le groupe organise régulièrement des activités, _after work beer_ et participe religieusement aux _pub quiz_ du [Mikkeller](https://mikkeller.com/). J'étais particulièrement surpris de leur ouverture. Dès la première journée au laboratoire, j'étais un des leurs. @@ -133,22 +132,20 @@ Les vols se remplissaient rapidement, le prix des billets d'avion montait en fl Samedi, le gouvernement demande à tous les citoyens canadiens de rentrer au pays. Merci les assurances (j'ai quand même réclamé, soyez sans crainte). - ### D'une pierre deux coups Un de mes objectifs de ce voyage était de rencontrer des gens. Du point de vue académique, c'était chose faite. Je voulais également en profiter pour rencontrer des gens de l'industrie. -Berlin bouillonne au niveau de la scène *startup* à la communauté NLP est impressionnante. +Berlin bouillonne au niveau de la scène _startup_ à la communauté NLP est impressionnante. J'ai eu la chance d'aller dîner avec Alan Nichol de chez [Rasa](https://rasa.com/). Ce fut une rencontre vraiment intéressante, Alan est **hyper** sympathique. On a parlé de la recherche qui se faisait chez Rasa ainsi que des projets qui m'excitaient. Il m'a même offert de venir faire un stage dans leur équipe de recherche! -Il prévoyait m'introduire à plusieurs *startups* pendant mon séjour à Berlin, dont [Spacy](https://spacy.io/). +Il prévoyait m'introduire à plusieurs _startups_ pendant mon séjour à Berlin, dont [Spacy](https://spacy.io/). Malheureusement, la pandémie m'a forcé à rentrer au bercail. - ## Take-aways Malgré tout ça, j'ai retiré beaucoup de positif de cette expérience. diff --git a/content/blog/2020-07-29-openlayer-one-year/index.fr.md b/content/blog/2020-07-29-openlayer-one-year/index.fr.md index 9d0fb9cad..0d6335faf 100644 --- a/content/blog/2020-07-29-openlayer-one-year/index.fr.md +++ b/content/blog/2020-07-29-openlayer-one-year/index.fr.md @@ -9,6 +9,7 @@ tags: [] description: "Retour sur la première année d'existence d'OpenLayer" featured: 'OpenLayer_YoutubeBanner.jpg' featuredpath: 'img/headers/' +aliases: ['/blog/2020-07-29-openlayer-one-year/podcast/'] --- Cela fait maintenant 1 an que j’ai lancé OpenLayer, mon projet de podcast vidéo sur l'IA. 32 épisodes plus tard, beaucoup de choses se sont dites derrière le micro. Ce texte débute par une présentation de mon voyage dans cette aventure et je termine avec un survol de certains moments forts des diverses discussions que j’ai eu la chance d’avoir avec les invités. diff --git a/content/blog/2020-08-19-train-a-sequence-model-with-poutyne/index.en.md b/content/blog/2020-08-19-train-a-sequence-model-with-poutyne/index.en.md index 0f71f2f6c..7fb723846 100644 --- a/content/blog/2020-08-19-train-a-sequence-model-with-poutyne/index.en.md +++ b/content/blog/2020-08-19-train-a-sequence-model-with-poutyne/index.en.md @@ -9,6 +9,7 @@ tags: [] description: 'Train an RNN for address parsing' featured: 'we-mastered-the-art-of-programming-cover.jpeg' featuredpath: 'img/headers/' +aliases: ['/blog/2020-08-19-train-a-sequence-model-with-poutyne/machine-learning/'] --- > In this article, we will train an RNN, or more precisely, an LSTM, to predict the sequence of tags associated with a diff --git a/content/blog/2020-10-30-reproducibility-in-ml-a-talk/index.fr.md b/content/blog/2020-10-30-reproducibility-in-ml-a-talk/index.fr.md index 8f293dee9..d2772e524 100644 --- a/content/blog/2020-10-30-reproducibility-in-ml-a-talk/index.fr.md +++ b/content/blog/2020-10-30-reproducibility-in-ml-a-talk/index.fr.md @@ -4,11 +4,12 @@ author: David Beauchemin date: '2020-11-11' slug: reproductibilite-apprentissage-automatique type: post -categories: ['Nouvelles', 'Machine learning'] -tags: [] +categories: ['Machine learning'] +tags: ['Nouvelles'] description: 'Des solutions aux embûches à la reproductibilité en apprentissage automatique' featured: 'hackathon-code-cover.JPG' featuredpath: 'img/headers/' +aliases: ['/blog/2020-10-30-reproducibility-in-ml-a-talk/machine-learning/'] --- > Un article rapide sur des solutions aux embûches à la reproductibilité en apprentissage automatique. diff --git a/content/blog/2021-02-01-tidynhl-analyse/index.fr.md b/content/blog/2021-02-01-tidynhl-analyse/index.fr.md index b06263898..145cd8c64 100644 --- a/content/blog/2021-02-01-tidynhl-analyse/index.fr.md +++ b/content/blog/2021-02-01-tidynhl-analyse/index.fr.md @@ -1,30 +1,26 @@ --- -title: "Repêchage de la LNH : est-ce vraiment une science inexacte?" -slug: "tidy-nhl-draft-analysis" -author: "Stéphane Caron" -description: "" -date: "2021-02-12" -categories: ["Analytique"] +title: 'Repêchage de la LNH : est-ce vraiment une science inexacte?' +slug: 'tidy-nhl-draft-analysis' +author: 'Stéphane Caron' +description: '' +date: '2021-02-12' +categories: ['Analytique'] type: post -tags: - - R - - Analytique de sports - - Hockey +tags: ['R', 'Analytique de sports', 'Hockey'] output: html_document: keep_md: yes -featured: "hockey-draft-post.jpg" -featuredpath: "img/headers/" +featured: 'hockey-draft-post.jpg' +featuredpath: 'img/headers/' reading_time: 15 +aliases: ['/blog/2021-02-01-tidynhl-analyse/tidy-nhl-draft-analysis/'] --- - - **On entend souvent dire que le repêchage de la LNH, tout comme c'est le cas dans d'autres sports professionnels, est une science inexacte. J'imagine que cette -expression fait référence au fait que si l'on doit repêcher un joueur $x$ à un moment -$t$ donné, il n'y a aucune certitude que ce joueur est le bon choix. Mais disons -qu'on prend le temps de regarder plusieurs choix, sur plusieurs années, est-ce toujours le +expression fait référence au fait que si l'on doit repêcher un joueur $x$ à un moment +$t$ donné, il n'y a aucune certitude que ce joueur est le bon choix. Mais disons +qu'on prend le temps de regarder plusieurs choix, sur plusieurs années, est-ce toujours le cas? Est-ce que le repêchage devient en quelque sorte une science plus "exacte" lorsque l'échantillon de joueurs repêchés devient plus grand?** Dans cet article, je tenterai d'éclairer cette question en analysant les récents @@ -34,8 +30,8 @@ _package_ Est-ce que certaines équipes sont plus performantes que d'autres pour repêcher de futurs joueurs? @@ -43,7 +39,6 @@ j'ai presque honte de l'avouer. J'adore analyser les futurs joueurs ainsi que le # Préparation des données {#preparation-donnees} Pour tenter de répondre à cette question, j'ai décidé d'analyser les sélections des repêchages allant de 2005 à 2015. Ce choix est arbitraire et se base sur le fait que 10 ans me semble assez crédible comme échantillon de joueurs (près de 2500 joueurs repêchés). Aussi, pour éviter que les bons joueurs repêchés dans de mauvaises équipes soient trop pénalisés, nous allons nous restreindre aux statistiques en saison régulière. Cela permettra aux joueurs d'être comparés sur une base plus équitable, où toutes les équipes jouent le même nombre de matchs. La première étape consiste à importer les données associées à ces repêchages avec la fonction `tidy_drafts()`. - ```r # Charger les packages @@ -66,12 +61,12 @@ dt_draft <- dt_draft[!is.na(player_id)] # On se crée une fonction pour fusionner les équipes déménagées (ou renommées) # Nettoyage de données merge_moved_teams <- function(dt) { - + dt[team_abbreviation %in% c("WPG", "ATL"), team_abbreviation := "WPG/ATL"] dt[team_abbreviation %in% c("PHX", "ARI"), team_abbreviation := "ARI/PHX"] - + dt - + } # Fusionner les équipes déménagés @@ -88,7 +83,7 @@ dt_draft[] ## 3: 2005 1 3 3 12 CAR 8471677 Jack Johnson USA USA U-18 ## 4: 2005 1 4 4 30 MIN 8471678 Benoit Pouliot OHL Sudbury ## 5: 2005 1 5 5 8 MTL 8471679 Carey Price WHL Tri-City -## --- +## --- ## 2334: 2015 7 26 207 8 MTL 8478921 Jeremiah Addison OHL Ottawa ## 2335: 2015 7 27 208 22 EDM 8478922 Miroslav Svoboda CZREP-JR. Trinec Jr. ## 2336: 2015 7 28 209 22 EDM 8478923 Ziyat Paigin RUSSIA Kazan @@ -98,11 +93,10 @@ dt_draft[] Afin d'avoir plus d'informations sur les joueurs, nous allons également importer des métadonnées (date de naissance, position, nationalité, etc.) sur ceux-ci grâce à la fonction `tidy_players_meta()`. - ```r # Obtenir les metadonnées sur les joueurs dt_meta_player <- tidy_players_meta( - players_id = dt_draft$player_id, + players_id = dt_draft$player_id, keep_id = TRUE ) @@ -110,8 +104,8 @@ dt_meta_player <- tidy_players_meta( merge_moved_teams(dt_meta_player) cols <- c("player_id", "player_position_type") -dt_draft[dt_meta_player, - (cols) := mget(cols), +dt_draft[dt_meta_player, + (cols) := mget(cols), on = .(player_id)] @@ -126,16 +120,15 @@ dt_meta_player[] ## 3: 8470817 William Colbert FALSE N 52 D D CAN CAN ON Arnprior 1985-02-06 FALSE 74 210 L FALSE FALSE NA FALSE NA ## 4: 8470872 Trevor Hendrikx FALSE N NA D D CAN CAN ON Winchester 1985-03-29 FALSE 74 200 R FALSE FALSE NA FALSE NA ## 5: 8470996 Danny Syvret FALSE Y 26 D D CAN CAN ON Millgrove 1985-06-13 FALSE 72 205 L FALSE FALSE NA FALSE NA -## --- +## --- ## 2328: 8478921 Jeremiah Addison FALSE N 64 L F CAN CAN ON Brampton 1996-10-21 FALSE 72 188 L TRUE FALSE NA FALSE NA ## 2329: 8478922 Miroslav Svoboda FALSE N 39 G G CZE CZE Vsetin 1995-03-07 FALSE 75 191 L TRUE FALSE NA FALSE NA ## 2330: 8478923 Ziyat Paigin FALSE N 92 D D RUS RUS Penza 1995-02-08 FALSE 78 210 L TRUE FALSE NA FALSE NA ## 2331: 8478924 Tate Olson FALSE N NA D D CAN CAN SK Saskatoon 1997-03-21 FALSE 74 174 L TRUE FALSE NA FALSE NA ## 2332: 8478925 John Dahlstrom FALSE N 44 R F SWE SWE Kungsbacka 1997-01-22 FALSE 72 189 L TRUE FALSE NA FALSE NA ``` - -Nous allons ensuite importer les statstiques individuelles de ces joueurs repêchés grâce aux fonctions `tidy_skaters_stats()` et `tidy_goalies_stats()`. +Nous allons ensuite importer les statstiques individuelles de ces joueurs repêchés grâce aux fonctions `tidy_skaters_stats()` et `tidy_goalies_stats()`. ```r # Obtenir les données de statistiques individuelles des joueurs @@ -152,8 +145,8 @@ dt_goalies_stats <- tidy_goalies_stats( keep_id = TRUE ) -dt_stats <- rbindlist(list(dt_skaters_stats, dt_goalies_stats), - use.names = TRUE, +dt_stats <- rbindlist(list(dt_skaters_stats, dt_goalies_stats), + use.names = TRUE, fill = TRUE) # Afficher un extrait des données @@ -167,7 +160,7 @@ dt_stats[] ## 3: 8470996 Danny Syvret 20062007 2006-07 regular 22 EDM 16 0 1 1 -10 6 295.500000 373 0 0 15 25 1 0 1 1 247.700000 0 0 0 28.300000 0 0 0 19.500000 NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA ## 4: 8470996 Danny Syvret 20082009 2008-09 regular 4 PHI 2 0 0 0 -1 0 18.850000 27 0 0 0 1 1 0 0 0 18.850000 0 0 0 0.000000 0 0 0 0.000000 NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA ## 5: 8470996 Danny Syvret 20092010 2009-10 regular 4 PHI 21 2 2 4 1 12 262.050000 357 0 0 14 20 6 2 2 4 257.516667 0 0 0 2.583333 0 0 0 1.950000 NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA -## --- +## --- ## 6717: 8478492 Ilya Samsonov 20202021 2020-21 regular 15 WSH NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA 2 2 1 0 0 1 0 53 7 0.8679245 3.360000 125.0000 43 6 0.8604651 8 1 0.8750000 2 0 1 ## 6718: 8478499 Adin Hill 20172018 2017-18 regular 53 ARI NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA 4 4 1 3 0 0 0 129 14 0.8914729 3.489097 240.7500 118 12 0.8983051 9 2 0.7777778 2 0 1 ## 6719: 8478499 Adin Hill 20182019 2018-19 regular 53 ARI NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA 13 11 7 5 0 0 1 322 32 0.9006211 2.757234 696.3500 271 27 0.9003690 41 5 0.8780488 10 0 1 @@ -178,25 +171,24 @@ dt_stats[] Finalement, nous allons appliquer quelques transformations et manipulations à ces trois jeux de données afin de régler quelques détails techniques importants pour l'analyse. - ```r # On se crée une fonction pour aggréger les données essentielles # On va devoir aggréger plusieurs fois les memes stats (pts, games, wins) dans l'analyse aggregate_stats <- function(dt, old_by_names, new_by_names) { - + dt_aggregated <- dt[, .( skater_points = sum(skater_points, na.rm = TRUE), skater_games = sum(skater_games, na.rm = TRUE), goalie_games = sum(goalie_games, na.rm = TRUE), goalie_wins = sum(goalie_wins, na.rm = TRUE) ), old_by_names] - + # On additionne les games des goalies et des players car stockés dans 2 variables différentes dt_aggregated[, player_games := ifelse(is.na(skater_games), 0, skater_games) + ifelse(is.na(goalie_games), 0, goalie_games)] setnames(dt_aggregated, old_by_names, new_by_names) - + dt_aggregated[] - + } # Fusionner les équipes déménagés @@ -205,13 +197,13 @@ merge_moved_teams(dt_stats) # Aggrégé les données par équipe jouée et joindre les données dt_stats_aggregated <- aggregate_stats( dt = dt_stats, - old_by_names = c("player_id", "team_abbreviation"), + old_by_names = c("player_id", "team_abbreviation"), new_by_names = c("player_id", "team_played") ) dt_all <- merge(dt_draft, dt_stats_aggregated, - by = "player_id", + by = "player_id", all.x = TRUE) setnames(dt_all, old = "team_abbreviation", new = "team_drafted") @@ -229,7 +221,7 @@ dt_all[player_name == "P.K. Subban"] Maintenant que nous avons structuré les données dans un format étant plus facile à manipuler, nous allons tenter d'aggréger celles-ci de différentes manières afin -d'avoir un portrait par équipe. Allons-y ! +d'avoir un portrait par équipe. Allons-y !
@@ -241,17 +233,16 @@ d'avoir un portrait par équipe. Allons-y ! Dans un premier temps, on est en droit de se poser la question: est-ce que toutes les équipes de la LNH repêchent autant de joueurs? Sachant que chaque équipe -possède un choix dans chacune des rondes du repêchage, on pourrait penser que +possède un choix dans chacune des rondes du repêchage, on pourrait penser que oui. Par contre, on sait très bien que les équipes peuvent échanger leurs choix afin d'obtenir des joueurs ou d'autres choix. Ainsi, j'étais curieux -de voir comment était distribué ce nombre de choix "réels" parmi les équipes. Pour +de voir comment était distribué ce nombre de choix "réels" parmi les équipes. Pour commencer, nous allons aggréger certaines informations par équipe. - ```r # Aggréger les données des jouers par équipes repêchés dt_per_team <- dt_all[, .( - nb_picks = uniqueN(.SD[]$player_id), + nb_picks = uniqueN(.SD[]$player_id), nb_1st_round_picks = uniqueN(.SD[draft_round == 1]$player_id), nb_games_played = sum(player_games, na.rm = TRUE), nb_points = sum(skater_points, na.rm = TRUE), @@ -273,10 +264,9 @@ head(dt_per_team[]) ``` Maintenant que nous avons les données aggrégées par équipe, nous pouvons -visualiser le nombre de joueurs repêchés grâce au _package_ [`ggplot2`](https://ggplot2.tidyverse.org). En bonus, on peut également voir le +visualiser le nombre de joueurs repêchés grâce au _package_ [`ggplot2`](https://ggplot2.tidyverse.org). En bonus, on peut également voir le nombre de joueurs repêchés en **première ronde** (points rouges). - ```r library(ggplot2) @@ -284,7 +274,7 @@ ggplot( data = dt_per_team, mapping = aes( x = nb_picks, - y = reorder(as.factor(team_drafted), nb_picks), + y = reorder(as.factor(team_drafted), nb_picks), ) ) + geom_col() + @@ -295,8 +285,8 @@ ggplot( ) ) + scale_color_manual( - name = "Nombre de joueurs choisi en 1er ronde", - values = c(" " = "red") + name = "Nombre de joueurs choisi en 1er ronde", + values = c(" " = "red") ) + labs( title = "Nombre de joueurs repêchés par équipe", @@ -319,16 +309,15 @@ l'objectif demeure de voir si certaines équipes sont plus performantes que les ## Les matchs joués dans la _Grande Ligue_ {#match-joues} -Comment savoir si une équipe repêche de bons joueurs? La première idée +Comment savoir si une équipe repêche de bons joueurs? La première idée qui me vient en tête est évidemment de regarder le nombre de matchs joué dans la LNH. Cette mesure est en quelque sorte indépendante de la position ou du style de joueur, ce qui rend son interprétation plus simple. Par contre, n'oublions pas que certaines équipes ont repêchés plus de joueurs que d'autres. Dans le graphique ci-dessous, si la logique du nombre de choix était respectée, les barres seraient ordonnées de la plus pâle vers la plus foncée (en partant d'en haut). - ```r ggplot( - data = dt_per_team, + data = dt_per_team, mapping = aes( - y = reorder(as.factor(team_drafted), nb_games_played), + y = reorder(as.factor(team_drafted), nb_games_played), x = nb_games_played, fill = nb_picks ) @@ -351,7 +340,6 @@ Je vous laisse tirer vos propres conclusions, mais de mon côté je remarque que Une autre mesure évidente à analyser est le nombre de points ($buts + passes$) obtenus par les joueurs repêchés par une équipe. Contrairement aux matchs joués, il faut tenir compte de la position du joueur dans ce cas-ci. Les attaquants font en général plus de points que les défenseurs, et peut-être que certaines équipes repêchent plus de défenseurs, ou même de gardiens (attendez ça s'en vient) ... - ```r library(dplyr) @@ -388,7 +376,6 @@ dt_stats_position[player_position_type %in% c("F", "D"),] %>% Dans le graphique ci-dessus, les équipes sont ordonnées selon le nombre de points combiné entre les attaquants (F) et les défenseurs (D). On peut donc conclure que les Oilers d'Edmonton est l'équipe qui a repêché, entre 2005 et 2015, les joueurs ayant récoltés le plus de points dans la LNH (merci à la lotterie). Dans ce graphique, on peut également voir que les Ducks d'Anaheim et les Predators de Nashville semblent avoir repêchés de bons défenseurs, probablement au détriment de repêcher de bons attaquants. Voici les 5 défenseurs les plus productifs repêchés par ces 2 équipes: - ```r unique(dt_all[team_drafted == "ANA" & player_position_type == "D"][order(-skater_points), player_name])[1:5] ``` @@ -411,7 +398,6 @@ Pas mal 😮! Maintenant que nous avons regardé les attaquants et les défenseurs, jetons un coup d'oeil aux gardiens de but. Comme mesure alternative au nombre de points, j'ai utilisé le nombre de victoires. - ```r aggregate_stats( dt = dt_all, @@ -441,7 +427,6 @@ aggregate_stats( Pour ceux qui pensaient que Carey Price permettrait au Canadiens d'être au premier rang, vous étiez trop ambitieux. Par contre, peut-être qu'ils auraient eu plus de chances si j'avais inclus le repêchage de 2003, séance où fut repeché un certain Jaroslav Halak. On peut remarquer que les Capitals de Washington semblent avoir eu du flair pour repêcher de bons gardiens de buts. Seriez-vous capable de nommer quelques uns de ces gardiens? - ```r unique(dt_all[team_drafted == "WSH" & player_position_type == "G"][order(-goalie_wins), player_name])[1:5] ``` @@ -456,7 +441,6 @@ Connaissant les problèmes de gardiens de but qu'on connu les Maple Leafs de Tor Maintenant que nous avons un premier portrait de la performance des équipes au repêchage, je veux valider une dernière chose. Je veux voir si certaines équipes ont tendance à faire plus souvent le "bon choix" que d'autres. Pour évaluer si une équipe fait le "bon choix", j'ai mis en place un petit algorithme. Cet algorithme peut s'expliquer comme suit: je regarde pour un choix donné, les choix subséquents et je valide qu'aucun joueur repêché après ce choix n'a fait plus de points. Pour les gardiens, je regarde le nombre de victoires. Pour paufiner cette approche, j'ai fais quelques hypothèses additionnelles: - ```r JOUEURS_FENETRE <- 20 JOUEURS_MIN_PTS <- 100 @@ -474,70 +458,67 @@ GARDIENS_MIN_WINS <- 50 Ces décisions un peu arbitraires sont basées sur mon jugement personnel. Je vous laisse le soin de changer certains de ces paramètres comme bon vous semble. Maintenant, voici la fonction qui nous permettra de tester notre approche. - - ```r define_good_choice <- function(dt, player_window, player_min_pts, goalie_min_wins, interval) { - + dt[, good_pick := NA] for (row in seq_len(nrow(dt))) { - + # On stock les informations sur le choix à valider draft_year_temp <- dt[row,]$draft_year draft_pick_temp <- dt[row,]$draft_overall draft_position_temp <- dt[row,]$player_position_type draft_nb_points_temp <- dt[row,]$skater_points draft_wins_temp <- dt[row,]$goalie_wins - + if (draft_position_temp %in% c("F", "D")) { - + # On filtre les joueurs repêchés dans la fenêtre dt_temp <- dt[draft_year == draft_year_temp & player_position_type == draft_position_temp & draft_overall <= (draft_pick_temp + player_window) & draft_overall > draft_pick_temp,] - + # On calcule le nombre de points maximal dans la fenetre max_pts_windows <- max(dt_temp$skater_points, na.rm = TRUE) - + # Si manquant, on remplace par 0 max_pts_windows <- ifelse(is.na(max_pts_windows), 0, max_pts_windows) draft_nb_points_temp <- ifelse(is.na(draft_nb_points_temp), 0, draft_nb_points_temp) - + if (draft_nb_points_temp > (max_pts_windows * interval) & draft_nb_points_temp >= player_min_pts) { dt[row,]$good_pick <- TRUE } else { dt[row,]$good_pick <- FALSE } - + } else if (draft_position_temp == "G") { - + # Aucune fenetre pour les gardiens dt_temp <- dt[draft_year == draft_year_temp & player_position_type == draft_position_temp & draft_overall > draft_pick_temp,] - + # On calcule le nombre de victoires maximal après le choix max_win_windows <- max(dt_temp$goalie_wins, na.rm = TRUE) - + # Si manquant, on remplace par 0 max_win_windows <- ifelse(is.na(max_win_windows), 0, max_win_windows) draft_wins_temp <- ifelse(is.na(draft_wins_temp), 0, draft_wins_temp) - + if (draft_wins_temp > (max_win_windows * interval) & draft_wins_temp >= goalie_min_wins) { dt[row,]$good_pick <- TRUE } else { dt[row,]$good_pick <- FALSE } - + } - + } - + dt[] - + } ``` Afin de valider que notre approche se comporte bel et bien comme souhaité, on peut jeter un apperçu aux 30 premiers choix du repêchage du 2005 et interpréter la colonne `good_pick`. - ```r new_draft_aggregation = aggregate_stats( dt = dt_all, @@ -596,12 +577,11 @@ dt_good_picks[draft_year == 2005][order(draft_overall)][1:30, .(draft_year, draf Maintenant, voyons voir quelles équipes ont réalisé le plus grand nombre de "bons choix" selon l'approche que nous proposons. Puisque certains "bons choix" pourraient être considérés meilleurs que d'autres "bons choix", j'ai pris le soin d'ajouter le nombre de points réalisés par ces joueurs dans le graphique ci-dessous. - ```r ggplot( - data = dt_good_picks[!is.na(good_pick), .(count = .N, nb_points = sum(skater_points, na.rm = T)), .(team_drafted, good_pick)][good_pick == TRUE], + data = dt_good_picks[!is.na(good_pick), .(count = .N, nb_points = sum(skater_points, na.rm = T)), .(team_drafted, good_pick)][good_pick == TRUE], mapping = aes( - x = count, + x = count, y = reorder(as.factor(team_drafted), count), fill = nb_points ) @@ -619,7 +599,6 @@ ggplot( On peut voir que certaines équipes ont eu plus de flair que d'autres. Les Blue Jackets de Columbus semblent se démarquer, regardons leurs "bons choix": - ```r columns_show <- c("draft_year", "draft_overall", "player_name", "skater_points", "goalie_wins") @@ -644,7 +623,6 @@ dt_good_picks[good_pick == TRUE & team_drafted == "CBJ", .SD, .SDcols = columns_ Il y a quand même quelques bons choix. Par contre, certains de ces joueurs ont peut-être profiter des failles de notre approche (Derek Dorsett ou John Moore par exemple). Je suis curieux de jeter un coup d'oeil à l'Avalanche du Colorado, qui ont realisé un peu moins de "bons choix". - ```r # Bons choix de l'Avalanche dt_good_picks[good_pick == TRUE & team_drafted == "COL", .SD, .SDcols = columns_show] @@ -664,8 +642,7 @@ dt_good_picks[good_pick == TRUE & team_drafted == "COL", .SD, .SDcols = columns_ Un peu moins de "bons choix" que les Blue Jackets, mais ceux-ci semblent avoir eu un impact bien plus grand. On peut dire que l'Avalanche a su profiter de leurs hauts choix au repêchage ... -Finalement, aviez-vous réussi à deviner quels étaient les 5 "bons choix" de notre Sainte-Flanelle? - +Finalement, aviez-vous réussi à deviner quels étaient les 5 "bons choix" de notre Sainte-Flanelle? ```r # Bons choix des Canadiens @@ -702,11 +679,9 @@ Pour conclure cet article, je dois vous avouer que je continue de croire que le ### Les grands "perdants" 👎 -- **Sabres de Buffalo**: Ils sont au 5ème rang pour le nombre de choix au total, (dont 13 en 1ère ronde), mais ils arrivent relativement loin dans le classement pour le nombre de points récoltés (21ème rang) ou pour les victoires des gardiens (19ème rang). Ils sont également en bas de peloton pour le nombre de "bons choix". +- **Sabres de Buffalo**: Ils sont au 5ème rang pour le nombre de choix au total, (dont 13 en 1ère ronde), mais ils arrivent relativement loin dans le classement pour le nombre de points récoltés (21ème rang) ou pour les victoires des gardiens (19ème rang). Ils sont également en bas de peloton pour le nombre de "bons choix". - **Jets de Winnipeg (et Atlanta)**: Ils sont au-dessus de la moyenne pour le nombre de choix total (83) et nombre de choix de première ronde (12). Ils se retrouvent en bas de classement pour la majorité des métriques: matchs (26ème rang), points (24ème rang). Seul point positif, les gardiens. - **Canucks de Vancouver**: Même s'ils ont eu peu de choix au total (68), ils n'ont pas su tirer leur épingle du jeu, et ce, dans aucune catégorie. Avec 12 choix de 1ère ronde, versus une moyenne de 11 dans la ligue, on aurait pu s'attendre à de meilleures performances. Le nombre de matchs joués par leurs joueurs repêchés est catastrophique ... - **Coyotes de l'Arizona (et Phoenix)**: On ne peut pas dire que leur performance est "désastreuse", mais étant l'équipe avec le plus de choix de premières rondes (16), je me serais attendu à mieux. - Mention honorable pour les "mal-aimés" Oilers d'Edmonton. Ils ont certainement eu beaucoup de choix "faciles", mais ils arrivent quand même au premier rang pour le nombre de points. - diff --git a/content/blog/2021-02-25-recap-2020/index.fr.md b/content/blog/2021-02-25-recap-2020/index.fr.md index a01dd91dc..1488f1ad7 100644 --- a/content/blog/2021-02-25-recap-2020/index.fr.md +++ b/content/blog/2021-02-25-recap-2020/index.fr.md @@ -4,16 +4,18 @@ author: Samuel Perreault et David Beauchemin date: '2021-03-03' slug: recap-2020 type: post -categories: ["Nouvelles .Layer"] +categories: ['Nouvelles .Layer'] tags: [] -description: "" -featured: "recap-2020-cover.png" -featuredpath: "img/headers/" +description: '' +featured: 'recap-2020-cover.png' +featuredpath: 'img/headers/' +aliases: ['/blog/2021-02-25-recap-2020/recap-2020/'] --- -Ce n'est une surprise pour personne, 2020 a été une année différente pour .Layer. Elle fut néanmoins une année assez remplie. Voici donc un petit *recap* pour la communauté. Comme on dit, « mieux vaut tard que jamais »! +Ce n'est une surprise pour personne, 2020 a été une année différente pour .Layer. Elle fut néanmoins une année assez remplie. Voici donc un petit _recap_ pour la communauté. Comme on dit, « mieux vaut tard que jamais »! + +# Administration de l'organisme sans but lucratif (OSBL) -# Administration de l'organisme sans but lucratif (OSBL) La crise planétaire de 2020 ne nous a pas empêché de tenir notre assemblée générale annuelle (AGA) dans un format en ligne. Durant l'AGA, plusieurs points importants ont été discutés, tels que la charte, le bilan financier, les projets en cours et futurs, et surtout la notion de membre actif. Ce dernier point, celui des membres actifs, était sans aucun doute le dernier morceau à mettre en place pour finaliser la structure de l'OSBL. Deux éléments motivent cette initiative : (1) couvrir les frais annuels liés à l'OSBL et (2) fournir un petit coussin pour les imprévus. En devenant membre membre actif, un individu obtiendra les avantages suivants : @@ -22,12 +24,13 @@ Ce dernier point, celui des membres actifs, était sans aucun doute le dernier m - la priorité de réservation pour les événements .Layer; - _Bragger_ que tu supportes les activités d'une OSBL en science des données (de loin le plus cool de la liste). -Une dizaine de personnes ont effectué les démarches pour devenir membres actifs (voir la liste [ici](https://github.com/dot-layer/charte-osbl/blob/master/LISTEMEMBRES.md)). Il est possible de s'y joindre en suivant cette [procédure](https://github.com/dot-layer/charte-osbl/blob/master/DEVENIRMEMBRE.md) qui se résume essentiellement à écrire à un courriel ton nom complet et à faire un virement interac de 20$ au trésorier (JTBai). +Une dizaine de personnes ont effectué les démarches pour devenir membres actifs (voir la liste [ici](https://github.com/dot-layer/charte-osbl/blob/master/LISTEMEMBRES.md)). Il est possible de s'y joindre en suivant cette [procédure](https://github.com/dot-layer/charte-osbl/blob/master/DEVENIRMEMBRE.md) qui se résume essentiellement à écrire à un courriel ton nom complet et à faire un virement interac de 20$ au trésorier (JTBai). # Comité blogue -Le comité blogue a connu une bonne première année d'existence. Ils ont supervisé la publication de 9 articles en 2020. Plusieurs améliorations esthétiques ont aussi été mises en place par le comité et surtout par [Annie Deshaies](https://github.com/AnnieDeshaies) (merci Annie, for real merci!). -On rappelle que le blogue est lieu de publication (en français ou en anglais) pour des articles portant sur des sujets variés : vulgarisation de concepts (apprentissage machine, programmation, science des données, statistiques, etc.), expérience personnelle, présentation d’un projet, *name it*! Ne soyez pas timide. En parlant de ces fameux articles, en voici la liste : +Le comité blogue a connu une bonne première année d'existence. Ils ont supervisé la publication de 9 articles en 2020. Plusieurs améliorations esthétiques ont aussi été mises en place par le comité et surtout par [Annie Deshaies](https://github.com/AnnieDeshaies) (merci Annie, for real merci!). + +On rappelle que le blogue est lieu de publication (en français ou en anglais) pour des articles portant sur des sujets variés : vulgarisation de concepts (apprentissage machine, programmation, science des données, statistiques, etc.), expérience personnelle, présentation d’un projet, _name it_! Ne soyez pas timide. En parlant de ces fameux articles, en voici la liste : - [Training a Recurrent Neural Network (RNN) using PyTorch](https://www.dotlayer.org/en/blog/2020-08-19-train-a-sequence-model-with-poutyne/machine-learning/) - [Reproductibilité en apprentissage automatique](https://www.dotlayer.org/blog/2020-10-30-reproducibility-in-ml-a-talk/machine-learning/) @@ -40,25 +43,31 @@ On rappelle que le blogue est lieu de publication (en français ou en anglais) p - [Argparse: un outil méconnu](https://www.dotlayer.org/blog/2019-05-21-argparse-package/optparse-package/) ### Projets futurs + Pour 2021, le comité vise un plus grand nombre de publications, soit une douzaine. De plus, cela fait [un moment](https://github.com/dot-layer/blog/issues/14) que nous parlons d'ajouter une bibliothèque de critiques (reviews) de livres, en espérant que cette année sera la bonne! Finalement, nous planifions aussi d'ajouter une section [nos membres](https://github.com/dot-layer/blog/issues/112) afin de remercier les membres actifs qui supportent l'initiative. Bien sûr, vos idées/suggestions sont toujours très appréciées. Pour ce faire, la meilleure façon est de créer une [issue](https://github.com/dot-layer/blog/issues) sur le dépôt du blogue. # OpenLayer -En 2019, [David Beauchemin](https://github.com/davebulaval) a introduit [OpenLayer](https://anchor.fm/open-layer), un podcast sur l'IA et l'apprentissage machine. Puisque la formule est en présentiel, le nombre d'épisodes publiés a été plus bas que prévu, soit 13 (au lieu de 40). Ceci donne tout de même un total de 32 épisodes pour le podcast! La formule a néanmoins connu un grand succès avec près de 5 000 vues sur YouTube et 2 000 sur les autres réseaux de diffusion (c.-à-d. Spotify et iTunes). Cette pause forcée a toutefois permis d'améliorer l'esthétique du podcast grâce à l'ajout de _thumbnails_, l'amélioration des messages de diffusion et le peaufinage de l'image. + +En 2019, [David Beauchemin](https://github.com/davebulaval) a introduit [OpenLayer](https://anchor.fm/open-layer), un podcast sur l'IA et l'apprentissage machine. Puisque la formule est en présentiel, le nombre d'épisodes publiés a été plus bas que prévu, soit 13 (au lieu de 40). Ceci donne tout de même un total de 32 épisodes pour le podcast! La formule a néanmoins connu un grand succès avec près de 5 000 vues sur YouTube et 2 000 sur les autres réseaux de diffusion (c.-à-d. Spotify et iTunes). Cette pause forcée a toutefois permis d'améliorer l'esthétique du podcast grâce à l'ajout de _thumbnails_, l'amélioration des messages de diffusion et le peaufinage de l'image. ### Vision future -Pour 2021, puisque la situation ne s'améliore pas et que la formule *en personne* habituelle n'est pas encore possible, une série _spéciale_ en ligne, en anglais, commence à être mise sur pied. L'idée est de « profiter » de l'impossibilité d'être en personne pour échanger avec des gens de l'extérieur du Québec. + +Pour 2021, puisque la situation ne s'améliore pas et que la formule _en personne_ habituelle n'est pas encore possible, une série _spéciale_ en ligne, en anglais, commence à être mise sur pied. L'idée est de « profiter » de l'impossibilité d'être en personne pour échanger avec des gens de l'extérieur du Québec. + # Meetup Machine Learning Québec + ![Meetup chez Can-Explore](fig/merge_img_can_explore.jpg) Crédit photo : Can-Explore -En 2019, nous écrivions : « 2020 s'annonce tout aussi occupée [que 2019] avec un meetup déjà *in the making*. On vise aussi à incorporer plus de formules 5@7 et potentiellement un hackaton. » +En 2019, nous écrivions : « 2020 s'annonce tout aussi occupée [que 2019] avec un meetup déjà _in the making_. On vise aussi à incorporer plus de formules 5@7 et potentiellement un hackaton. » _I guess not!_ Finalement, nous avons _**seulement**_ eu droit à un évènement meetup, le 12 février chez Can-Explore. L'évènement fut un grand succès avec une cinquantaine de personnes (le bon vieux temps quoi). La gang de Meetup ML a décidé de ne pas organiser d'évènement en ligne puisque la formule originale est très axée sur les échanges pendant et après les présentations; l'évènement en ligne nous semblait dénaturé. Nous avons préféré concentrer nos efforts sur autre chose : nos 10 autres projets. Nous restons bien sûr à l'affut de tout changement et espérons organiser un meetup vers la fin de 2021. # Stage sur le projet Universal Data Tool -Officiellement, .Layer a comme mission de promouvoir la collaboration et le partage de connaissances dans le domaine de la science des données. Ceci veut dire, entre autres choses, d'appuyer (1) des stagiaires dans leur développement et (2) des projets *open-source*. On souligne donc l'initiative de [Marc-Alexandre Paquet](https://github.com/Ownmarc) (qui a pondu cette idée de génie) et David Beauchemin (on s'en sort pas, encore lui) qui ont utilisé le statut d'OSBL de .Layer pour offrir un stage en programmation de cinq semaines à Cedric Jean. Cedric a travaillé sur l'outil d'annotation de données [Universal Data Tool](https://universaldatatool.com/). Son travail a permis l'intégration de [plusieurs fonctionnalités](https://github.com/UniversalDataTool/universal-data-tool/issues?q=CedricJean) telle que la [gestion Cognito S3](https://github.com/UniversalDataTool/universal-data-tool/pull/103). + +Officiellement, .Layer a comme mission de promouvoir la collaboration et le partage de connaissances dans le domaine de la science des données. Ceci veut dire, entre autres choses, d'appuyer (1) des stagiaires dans leur développement et (2) des projets _open-source_. On souligne donc l'initiative de [Marc-Alexandre Paquet](https://github.com/Ownmarc) (qui a pondu cette idée de génie) et David Beauchemin (on s'en sort pas, encore lui) qui ont utilisé le statut d'OSBL de .Layer pour offrir un stage en programmation de cinq semaines à Cedric Jean. Cedric a travaillé sur l'outil d'annotation de données [Universal Data Tool](https://universaldatatool.com/). Son travail a permis l'intégration de [plusieurs fonctionnalités](https://github.com/UniversalDataTool/universal-data-tool/issues?q=CedricJean) telle que la [gestion Cognito S3](https://github.com/UniversalDataTool/universal-data-tool/pull/103). Message aux membres .Layer, si vous êtes intéressés à encadrer un étudiant (pour un projet lié aux valeurs de l'organisation), mais qu'il vous manque un cadre formel pour officialiser le tout, n'hésitez pas à en parler à un membre du CA. Mentionnons [Marc-Alexandre Paquet](https://github.com/Ownmarc) qui a eu (1) l'idée et (2) qui a fortement contribué à la supervision du stagiaire. From 3a004358b21186543873ed32a9dfea9d48b7fe41 Mon Sep 17 00:00:00 2001 From: Annie Deshaies Date: Fri, 5 Mar 2021 08:25:37 -0500 Subject: [PATCH 3/7] change how to --- content/blog/2020-03-19-howto/index.en.md | 2 +- content/blog/2020-03-19-howto/index.fr.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/content/blog/2020-03-19-howto/index.en.md b/content/blog/2020-03-19-howto/index.en.md index 2c2b7094c..7255edf64 100644 --- a/content/blog/2020-03-19-howto/index.en.md +++ b/content/blog/2020-03-19-howto/index.en.md @@ -58,7 +58,7 @@ reading_time: "" The fields are almost all _self-explanatory_. The `date` field should contain the creation date of the file. We will change it for the publication date in due time (in the _Pull Request_). We will also take care of the `categories` and` tags` fields. -The `slug` field is a _nickname_ for your post, which will be used to name the various files linked to the post in the blog directory. +The `slug` field is a _nickname_ for your post, which will be used to name the various files linked to the post in the blog directory and the url name. The `featured` field should contain the name of the cover image file, while` featuredpath` (which should remain unchanged) indicates where to find the file. This is where you should place your cover image. Finally, if the post submitted is already published on another platform, please add the `canonical` field to specify the platform. For example, in the header of the post _What's wrong with Scikit-Learn_ published on the blog, we find diff --git a/content/blog/2020-03-19-howto/index.fr.md b/content/blog/2020-03-19-howto/index.fr.md index 81c6ce34a..80e6f3dbb 100644 --- a/content/blog/2020-03-19-howto/index.fr.md +++ b/content/blog/2020-03-19-howto/index.fr.md @@ -58,7 +58,7 @@ reading_time: "" Les champs sont presque tous _self-explanatory_. Le champ `date` devrait contenir la date de création du fichier. On le changera pour la date de publication en temps et lieu (dans le _Pull Request_). On s'occupera aussi des champs `categories` et `tags`. -Le champ `slug` est un _surnom_ pour votre article qui sera utilisé pour nommer les différents dossiers liés à l'article sur le répertoire du blog. +Le champ `slug` est un _surnom_ pour votre article qui sera utilisé pour nommer les différents dossiers liés à l'article sur le répertoire du blog et le nom de l'url. Le champ `featured` doit contenir le nom du fichier de l'image de couverture, tandis que `featuredpath` (qui doit rester inchangé) indique où trouver le fichier. C'est d'ailleurs là que vous devez placer votre image de couverture. Finalement, si l'article soumis est déjà publié sur une autre plateforme, veuillez ajouter le champ `canonical` afin de spécifier ladite plateforme. Par exemple, dans l'en-tête de l'article _What's wrong with Scikit-Learn_ publié sur le blog, on trouve From fb51785b20d0ac25b8646490c513b0c6f3f16250 Mon Sep 17 00:00:00 2001 From: Annie Deshaies Date: Fri, 5 Mar 2021 09:24:31 -0500 Subject: [PATCH 4/7] :fire: --- .../index.en.md | 53 +++++++++++++++++++ .../index.fr.md | 37 +++++++------ layouts/book/content-list.html | 13 ----- layouts/book/header.html | 31 ----------- layouts/bookshelf/content-list.html | 17 ++++++ .../{book => bookshelf}/content-single.html | 0 layouts/{book => bookshelf}/featured.html | 0 .../{book => bookshelf}/footer-category.html | 0 layouts/bookshelf/header.html | 38 +++++++++++++ layouts/bookshelf/list.html | 28 ---------- layouts/{book => bookshelf}/prev-next.html | 0 layouts/{book => bookshelf}/single.html | 0 static/css/add-on.css | 1 - 13 files changed, 126 insertions(+), 92 deletions(-) create mode 100644 content/bookshelf/2019-01-06-an-introduction-to-statistical-learning/index.en.md delete mode 100644 layouts/book/content-list.html delete mode 100644 layouts/book/header.html create mode 100644 layouts/bookshelf/content-list.html rename layouts/{book => bookshelf}/content-single.html (100%) rename layouts/{book => bookshelf}/featured.html (100%) rename layouts/{book => bookshelf}/footer-category.html (100%) create mode 100644 layouts/bookshelf/header.html delete mode 100644 layouts/bookshelf/list.html rename layouts/{book => bookshelf}/prev-next.html (100%) rename layouts/{book => bookshelf}/single.html (100%) diff --git a/content/bookshelf/2019-01-06-an-introduction-to-statistical-learning/index.en.md b/content/bookshelf/2019-01-06-an-introduction-to-statistical-learning/index.en.md new file mode 100644 index 000000000..f3ceec18e --- /dev/null +++ b/content/bookshelf/2019-01-06-an-introduction-to-statistical-learning/index.en.md @@ -0,0 +1,53 @@ +--- +title: 'Book Review: An Introduction to Statistical Learning' +author: 'Christopher Blier-Wong' +description: 'Gareth James, Daniela Witten, Trevor Hastie and Robert Tibshirani' +date: '2019-01-06' +type: 'bookshelf' +tags: ['Machine Learning', 'Statistics', 'Textbook', 'R'] +slug: islr +summary: 'This book is mostly interested in supervised learning, the task of +using statistical models to predict the relationship between +predictors and a response. Given there is a function $f$ that +provides a relationship between explanatory variables $X$ and a +response variable $Y$, what are statistical methods for estimating +$f$.' +--- + +**Ranking**: :star: :star: :star: :star: :star: + +**Coding prerequisites**: low + +**Math/stats prerequisites**: low + +**Level**: easy + +# Summary + +This book is mostly interested in supervised learning, the task of +using statistical models to predict the relationship between +predictors and a response. Given there is a function $f$ that +provides a relationship between explanatory variables $X$ and a +response variable $Y$, what are statistical methods for estimating +$f$. + +# Review + +There are two steps in the education of a data scientist : +understanding statistical models, and putting them in practice. +This book definitely helps on the practice part, assuming little +mathematical or statistical knowledge from the reader. This book +is a great companion to it's big brother, The Elements of Statistical +Learning. First of all, R is a great language to start with +statistical learning as it's really easy to manipulate data. + +Exercices are simple but labs are great for understanding and as a +way to + +I particularly liked their chapter 5, treating resampling methods. +Most of us know what $k$-fold cross validation is, but how and why +it works isn't understood by all. This chapter helps us understand +the bias-variance tradeoff for the choice of $k$ in the method. This +is an example of a situation does not only show the method, but +also examines their statistical properties, something not all machine +learning books do. diff --git a/content/bookshelf/2019-01-06-an-introduction-to-statistical-learning/index.fr.md b/content/bookshelf/2019-01-06-an-introduction-to-statistical-learning/index.fr.md index 93dd2e7c0..547a6ea7f 100644 --- a/content/bookshelf/2019-01-06-an-introduction-to-statistical-learning/index.fr.md +++ b/content/bookshelf/2019-01-06-an-introduction-to-statistical-learning/index.fr.md @@ -1,27 +1,26 @@ --- -title: "Book Review: An Introduction to Statistical Learning" -slug: islr -author: "Christopher Blier-Wong" -description: "Gareth James, Daniela Witten, Trevor Hastie and Robert Tibshirani" +title: 'Book Review: An Introduction to Statistical Learning' +author: 'Christopher Blier-Wong' +description: 'Gareth James, Daniela Witten, Trevor Hastie and Robert Tibshirani' date: '2019-01-06' -type: "book review" -tags: -- Machine Learning -- Statistics -- Textbook -- R -output: - html_document: - keep_md: true +type: 'bookshelf' +tags: ['Machine Learning', 'Statistics', 'Textbook', 'R'] +slug: islr +summary: 'This book is mostly interested in supervised learning, the task of +using statistical models to predict the relationship between +predictors and a response. Given there is a function $f$ that +provides a relationship between explanatory variables $X$ and a +response variable $Y$, what are statistical methods for estimating +$f$.' --- - > Ranking: :star: :star: :star: :star: :star: +**Ranking**: :star: :star: :star: :star: :star: - > Coding prerequisites : low - - > Math/stats prerequisites : low - - > Level : easy +**Coding prerequisites**: low + +**Math/stats prerequisites**: low + +**Level**: easy # Summary diff --git a/layouts/book/content-list.html b/layouts/book/content-list.html deleted file mode 100644 index 634e7dda7..000000000 --- a/layouts/book/content-list.html +++ /dev/null @@ -1,13 +0,0 @@ -
- {{ .Render "header" }} - {{ .Render "featured" }} - {{ $.Scratch.Set "summary" ((delimit (findRE "(.|\n)*?

" .Content 1) "") | truncate (default 500 .Site.Params.summary_length) (default "…" .Site.Params.text.truncated ) | replaceRE "&" "&" | safeHTML) }} - {{ $.Scratch.Get "summary" }} - - -
\ No newline at end of file diff --git a/layouts/book/header.html b/layouts/book/header.html deleted file mode 100644 index a941f0909..000000000 --- a/layouts/book/header.html +++ /dev/null @@ -1,31 +0,0 @@ -
-
- {{ if $.Scratch.Get "h1" }} -

{{ .Title }}

- {{ $.Scratch.Set "h1" false }} - {{ else }} -

{{ .Title }}

- {{ end }} - {{ with .Description }} -

{{ . }}

- {{ end }} -
-
- {{if eq .Site.Language.Lang "fr"}} - {{ $monthFr := index $.Site.Data.mois.mois }} - - {{ else }} - - {{ end }} - {{ .Params.author | safeHTML }} - {{ if .Site.Params.includeReadingTime }} -

{{ .ReadingTime }} {{ i18n "postreadingtime" }}

- {{ end }} -
-
\ No newline at end of file diff --git a/layouts/bookshelf/content-list.html b/layouts/bookshelf/content-list.html new file mode 100644 index 000000000..21fb8e86f --- /dev/null +++ b/layouts/bookshelf/content-list.html @@ -0,0 +1,17 @@ +
+ {{ .Render "header" }} + {{ .Render "featured" }} + {{ .Summary }} + +
+ {{ $.Scratch.Set "ranking" ((delimit (findRE "(.|\n)*?

" .Content 1) "") | truncate (default 500) (default "…" .Site.Params.text.truncated ) | replaceRE "&" "&" | safeHTML) }} + {{ $.Scratch.Get "ranking" }} +
+ + +
diff --git a/layouts/book/content-single.html b/layouts/bookshelf/content-single.html similarity index 100% rename from layouts/book/content-single.html rename to layouts/bookshelf/content-single.html diff --git a/layouts/book/featured.html b/layouts/bookshelf/featured.html similarity index 100% rename from layouts/book/featured.html rename to layouts/bookshelf/featured.html diff --git a/layouts/book/footer-category.html b/layouts/bookshelf/footer-category.html similarity index 100% rename from layouts/book/footer-category.html rename to layouts/bookshelf/footer-category.html diff --git a/layouts/bookshelf/header.html b/layouts/bookshelf/header.html new file mode 100644 index 000000000..770289854 --- /dev/null +++ b/layouts/bookshelf/header.html @@ -0,0 +1,38 @@ +
+
+ {{ if $.Scratch.Get "h1" }} +

{{ .Title }}

+ {{ $.Scratch.Set "h1" false }} + {{ else }} +

{{ .Title }}

+ {{ end }} + {{ with .Description }} +

{{ . }}

+ {{ end }} +
+
+ {{if eq .Site.Language.Lang "fr"}} + {{ $monthFr := index $.Site.Data.mois.mois }} + + {{ else }} + + {{ end }} + + {{ .Params.author | safeHTML }} + {{ if .Params.reading_time }} +

+ {{ .Params.reading_time }} + {{ i18n "postreadingtime" }} +

+ {{ else }} +

+ {{ .ReadingTime }} + {{ i18n "postreadingtime" }} +

+ {{ end }} +
+
diff --git a/layouts/bookshelf/list.html b/layouts/bookshelf/list.html deleted file mode 100644 index 39faba9d8..000000000 --- a/layouts/bookshelf/list.html +++ /dev/null @@ -1,28 +0,0 @@ -{{ partial "general-title" . }} - -{{ partial "header" . }} -{{ partial "navbar" . }} - -
-
-
-
- {{ if $.Scratch.Get "h1" }} -

{{ .Title }}

- {{ $.Scratch.Set "h1" false }} - {{ else }} -

{{ .Title }}

- {{ end }} - {{ with .Description }} -

{{ . }}

- {{ end }} -
-
-
- {{ .Content }} -

{{ i18n "in_construction" }}!

-
-
-
-{{ partial "sidebar" . }} -{{ partial "footer" . }} \ No newline at end of file diff --git a/layouts/book/prev-next.html b/layouts/bookshelf/prev-next.html similarity index 100% rename from layouts/book/prev-next.html rename to layouts/bookshelf/prev-next.html diff --git a/layouts/book/single.html b/layouts/bookshelf/single.html similarity index 100% rename from layouts/book/single.html rename to layouts/bookshelf/single.html diff --git a/static/css/add-on.css b/static/css/add-on.css index 3f522a0b8..4b0a5b82f 100644 --- a/static/css/add-on.css +++ b/static/css/add-on.css @@ -129,7 +129,6 @@ h1, h2, h3, h4, h5, h6 { -webkit-justify-content: space-between; -ms-justify-content: space-between; justify-content: space-between; - margin-top: 2em; } .stats .post_category { From 9780e4c0a166251ebabc5bfb17c0809f74571c91 Mon Sep 17 00:00:00 2001 From: Annie Deshaies Date: Fri, 5 Mar 2021 09:29:47 -0500 Subject: [PATCH 5/7] :cat: --- config.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/config.toml b/config.toml index f2b02f1d8..4014e9ceb 100644 --- a/config.toml +++ b/config.toml @@ -15,6 +15,7 @@ enableEmoji = true [permalinks] blog = "/:slug/" + bookshelf = "/:slug/" [params] From c5d051f8c300cf196b0a1fdd334e899cc799fd41 Mon Sep 17 00:00:00 2001 From: Annie Deshaies Date: Tue, 9 Mar 2021 09:33:23 -0500 Subject: [PATCH 6/7] empty bookshelf --- .../index.fr 2.md | 55 ------------------- .../index.fr.md | 54 ------------------ content/bookshelf/_index.en.md | 2 +- .../index.en.md | 0 4 files changed, 1 insertion(+), 110 deletions(-) delete mode 100644 content/bookshelf/2019-01-06-an-introduction-to-statistical-learning/index.fr 2.md delete mode 100644 content/bookshelf/2019-01-06-an-introduction-to-statistical-learning/index.fr.md rename content/{bookshelf => test}/2019-01-06-an-introduction-to-statistical-learning/index.en.md (100%) diff --git a/content/bookshelf/2019-01-06-an-introduction-to-statistical-learning/index.fr 2.md b/content/bookshelf/2019-01-06-an-introduction-to-statistical-learning/index.fr 2.md deleted file mode 100644 index 93dd2e7c0..000000000 --- a/content/bookshelf/2019-01-06-an-introduction-to-statistical-learning/index.fr 2.md +++ /dev/null @@ -1,55 +0,0 @@ ---- -title: "Book Review: An Introduction to Statistical Learning" -slug: islr -author: "Christopher Blier-Wong" -description: "Gareth James, Daniela Witten, Trevor Hastie and Robert Tibshirani" -date: '2019-01-06' -type: "book review" -tags: -- Machine Learning -- Statistics -- Textbook -- R -output: - html_document: - keep_md: true ---- - - > Ranking: :star: :star: :star: :star: :star: - - > Coding prerequisites : low - - > Math/stats prerequisites : low - - > Level : easy - - -# Summary - -This book is mostly interested in supervised learning, the task of -using statistical models to predict the relationship between -predictors and a response. Given there is a function $f$ that -provides a relationship between explanatory variables $X$ and a -response variable $Y$, what are statistical methods for estimating -$f$. - -# Review - -There are two steps in the education of a data scientist : -understanding statistical models, and putting them in practice. -This book definitely helps on the practice part, assuming little -mathematical or statistical knowledge from the reader. This book -is a great companion to it's big brother, The Elements of Statistical -Learning. First of all, R is a great language to start with -statistical learning as it's really easy to manipulate data. - -Exercices are simple but labs are great for understanding and as a -way to - -I particularly liked their chapter 5, treating resampling methods. -Most of us know what $k$-fold cross validation is, but how and why -it works isn't understood by all. This chapter helps us understand -the bias-variance tradeoff for the choice of $k$ in the method. This -is an example of a situation does not only show the method, but -also examines their statistical properties, something not all machine -learning books do. diff --git a/content/bookshelf/2019-01-06-an-introduction-to-statistical-learning/index.fr.md b/content/bookshelf/2019-01-06-an-introduction-to-statistical-learning/index.fr.md deleted file mode 100644 index 547a6ea7f..000000000 --- a/content/bookshelf/2019-01-06-an-introduction-to-statistical-learning/index.fr.md +++ /dev/null @@ -1,54 +0,0 @@ ---- -title: 'Book Review: An Introduction to Statistical Learning' -author: 'Christopher Blier-Wong' -description: 'Gareth James, Daniela Witten, Trevor Hastie and Robert Tibshirani' -date: '2019-01-06' -type: 'bookshelf' -tags: ['Machine Learning', 'Statistics', 'Textbook', 'R'] -slug: islr -summary: 'This book is mostly interested in supervised learning, the task of -using statistical models to predict the relationship between -predictors and a response. Given there is a function $f$ that -provides a relationship between explanatory variables $X$ and a -response variable $Y$, what are statistical methods for estimating -$f$.' ---- - -**Ranking**: :star: :star: :star: :star: :star: - -**Coding prerequisites**: low - -**Math/stats prerequisites**: low - -**Level**: easy - - -# Summary - -This book is mostly interested in supervised learning, the task of -using statistical models to predict the relationship between -predictors and a response. Given there is a function $f$ that -provides a relationship between explanatory variables $X$ and a -response variable $Y$, what are statistical methods for estimating -$f$. - -# Review - -There are two steps in the education of a data scientist : -understanding statistical models, and putting them in practice. -This book definitely helps on the practice part, assuming little -mathematical or statistical knowledge from the reader. This book -is a great companion to it's big brother, The Elements of Statistical -Learning. First of all, R is a great language to start with -statistical learning as it's really easy to manipulate data. - -Exercices are simple but labs are great for understanding and as a -way to - -I particularly liked their chapter 5, treating resampling methods. -Most of us know what $k$-fold cross validation is, but how and why -it works isn't understood by all. This chapter helps us understand -the bias-variance tradeoff for the choice of $k$ in the method. This -is an example of a situation does not only show the method, but -also examines their statistical properties, something not all machine -learning books do. diff --git a/content/bookshelf/_index.en.md b/content/bookshelf/_index.en.md index f91efe584..399ffd8b2 100644 --- a/content/bookshelf/_index.en.md +++ b/content/bookshelf/_index.en.md @@ -2,4 +2,4 @@ title = "Bookshelf" type = "book review" date = "2020-01-03" -+++ \ No newline at end of file ++++ diff --git a/content/bookshelf/2019-01-06-an-introduction-to-statistical-learning/index.en.md b/content/test/2019-01-06-an-introduction-to-statistical-learning/index.en.md similarity index 100% rename from content/bookshelf/2019-01-06-an-introduction-to-statistical-learning/index.en.md rename to content/test/2019-01-06-an-introduction-to-statistical-learning/index.en.md From 86a39a96c76d71e3b0776e842e6a8ab87fdba9bc Mon Sep 17 00:00:00 2001 From: davebulaval Date: Tue, 9 Mar 2021 09:48:35 -0500 Subject: [PATCH 7/7] added example for bookshelf --- .../2021-03-09-an-example/index.en.md | 26 +++++++++++++++++++ .../2021-03-09-an-example/index.fr.md | 26 +++++++++++++++++++ .../index.fr.md | 26 +++++++++++++++++++ 3 files changed, 78 insertions(+) create mode 100644 content/bookshelf/2021-03-09-an-example/index.en.md create mode 100644 content/bookshelf/2021-03-09-an-example/index.fr.md create mode 100644 content/test/2019-01-06-an-introduction-to-statistical-learning/index.fr.md diff --git a/content/bookshelf/2021-03-09-an-example/index.en.md b/content/bookshelf/2021-03-09-an-example/index.en.md new file mode 100644 index 000000000..ddb4a6f18 --- /dev/null +++ b/content/bookshelf/2021-03-09-an-example/index.en.md @@ -0,0 +1,26 @@ +--- +title: 'Book Review: An Example' +author: 'David Beauchemin and Annie Deshaies' +description: 'Jon Doe and Jone Doe' +date: '2021-03-09' +type: 'bookshelf' +tags: ['Machine Learning', 'tags2', 'tags3', 'Python'] +slug: example +summary: 'This book review is an example for others to do a book review.' +--- + +**Ranking**: :star: :star: :star: :star: :star: + +**Coding prerequisites**: low + +**Math/stats prerequisites**: low + +**Level**: easy + +# Summary + +This book review is an example for others to do a book review. + +# Review + +Here we would write down the review of the book. Talking about what we liked, what we did not like and other interesting stuff. \ No newline at end of file diff --git a/content/bookshelf/2021-03-09-an-example/index.fr.md b/content/bookshelf/2021-03-09-an-example/index.fr.md new file mode 100644 index 000000000..33d265778 --- /dev/null +++ b/content/bookshelf/2021-03-09-an-example/index.fr.md @@ -0,0 +1,26 @@ +--- +title: 'Critique de livres : Un exemple' +author: 'David Beauchemin et Annie Deshaies' +description: 'Jon Doe et Jone Doe' +date: '2021-03-09' +type: 'bookshelf' +tags: ['apprentissage automatique', 'tags2', 'tags3', 'Python'] +slug: exemple +summary: 'Cette critique de livre est un exemple pour d'autres de faire une critique de livre.' +--- + +**Appréciation** : :star : :star : :star : :star : :star : + +**Niveau de difficulté de programmation** : faible + +**Prérequis maths/statistiques** : faible + +**Niveau** : facile + +# Résumé + +Cette critique de livre est un exemple pour d'autres de faire une critique de livre. + +# Critique + +Ici, on écrirait la critique du livre. Nous pourrions parler de ce que nous avons aimé, de ce que nous n'avons pas aimé et d'autres choses intéressantes. \ No newline at end of file diff --git a/content/test/2019-01-06-an-introduction-to-statistical-learning/index.fr.md b/content/test/2019-01-06-an-introduction-to-statistical-learning/index.fr.md new file mode 100644 index 000000000..33d265778 --- /dev/null +++ b/content/test/2019-01-06-an-introduction-to-statistical-learning/index.fr.md @@ -0,0 +1,26 @@ +--- +title: 'Critique de livres : Un exemple' +author: 'David Beauchemin et Annie Deshaies' +description: 'Jon Doe et Jone Doe' +date: '2021-03-09' +type: 'bookshelf' +tags: ['apprentissage automatique', 'tags2', 'tags3', 'Python'] +slug: exemple +summary: 'Cette critique de livre est un exemple pour d'autres de faire une critique de livre.' +--- + +**Appréciation** : :star : :star : :star : :star : :star : + +**Niveau de difficulté de programmation** : faible + +**Prérequis maths/statistiques** : faible + +**Niveau** : facile + +# Résumé + +Cette critique de livre est un exemple pour d'autres de faire une critique de livre. + +# Critique + +Ici, on écrirait la critique du livre. Nous pourrions parler de ce que nous avons aimé, de ce que nous n'avons pas aimé et d'autres choses intéressantes. \ No newline at end of file