# Basic Concepts
The bottom-up analysis of dynamic states of a network is based on network topology and kinetic theory describing the links in the network. In this chapter, we provide a primer for the basic concepts of dynamic analysis of network states. We also discuss basics of the kinetic theory that is needed to formulate and understand detailed dynamic models of biochemical reaction networks.

## Properties of Dynamic States
The three key dynamic properties outlined in the introduction - time constants, aggregate variables and transitions - are detailed in this section.

### Time scales
A fundamental quantity in dynamic analysis is the _time constant_. A time constant is a measure of time span over which significant changes occur in a state variable. It is thus a scaling factor for time and determines where in the time scale spectrum one needs to focus attention when dealing with a particular process or event of interest.

A general definition of a time constant is given by

$$\begin{equation} \tau = \frac{\Delta x}{|dx/dt|_{avg}} \tag{2.1} \end{equation}$$

where $\Delta{x}$ is a characteristic change in the state variable $x$ of interest and $|dx/dt|_{avg}$
is an estimate of the rate of change of the variable $x$. Notice the ratio between $\Delta{x}$ and the average derivative has units of time, and the time constant characterizes the time span over which these changes in $x$ occur, see Figure 2.1.

**Figure 2.1:** Illustration of the concept of a time constant, $\tau$, and its estimation as $\tau = \Delta x\ / |dx/dt|_{avg}.$

In a network, there are many time constants. In fact, there is a spectrum of time constants, $\tau_1,\ \tau_2, \dots \tau_r$ where $r$ is the rank of the Jacobian matrix defining the dynamic dimensionality of the dynamic response of the network. This spectrum of time constants typically spans many orders of magnitude. The consequences of a well-separated set of time constants is a key concern in the analysis of network dynamics.

### Forming aggregate variables through "pooling"
One important consequence of time scale hierarchy is the fact that we will have fast and slow events. If fast events are filtered out or ignored, one removes a dynamic degree of freedom from the dynamic description, thus reducing the dynamic dimension of a system. Removal of a dynamic dimension leads to "coarse-graining" of the dynamic description. Reduction in dynamic dimension results in the combination, or pooling, of variables into aggregate variables.

A simple example can be obtained from upper glycolysis. The first three reactions of this pathway are:

$$\begin{equation} \text{glucose}\ \underset{\stackrel{\frown}{ATP \ ADP}}{\stackrel{HK}{\longrightarrow}} \text{G6P} \underset{\text{fast}, \tau_f}{\stackrel{PGI}{\leftrightharpoons}} \text{F6P} \underset{\stackrel{\frown}{ATP \ ADP}}{\stackrel{PFK}{\longrightarrow}} \text{FDP} \tag{2.2} \end{equation}$$

This schema includes the second step in glycolysis where glucose-6-phosphate (G6P) is converted to fructose-6-phosphate (F6P) by the phosphogluco-isomerase (PGI). Isomerases are highly active enzymes and have rate constants that tend to be fast. In this case, PGI has a much faster response time than the response time of the flanking kinases in this pathway, hexokinase (HK) and phosphofructokinase (PFK). If one considers a time period that is much greater than $\tau_f$ (the time constant associated with PGI), this system is simplified to:

$$\begin{equation} \underset{\stackrel{\frown}{ATP \ ADP}}{\stackrel{HK}{\longrightarrow}} \ \underset{t \gg \tau_f}{\text{HP}} \ \underset{\stackrel{\frown}{ATP \ ADP}}{\stackrel{PFK}{\longrightarrow}} \tag{2.3} \end{equation}$$

where HP = (G6P+F6P) is the hexosephosphate pool. At a slow time scale (i.e, long compared to $\tau_f$), the isomerase reaction has effectively equilibrated, leading to the removal of its dynamics from the network. As a result, F6P and G6P become dynamically coupled and can be considered to be a single variable. HP is an example of an aggregate variable that results from pooling G6P and F6P into a single variable. Such aggregation of variables is a consequence of time-scale hierarchy in networks. Determining how to aggregate variables into meaningful quantities becomes an important consideration in the dynamic analysis of network states. Further examples of pooling variables are given in Section 2.3.

### Transitions
The dynamic analysis of a network comes down to examining its transient behavior as it moves from one state to another.

**Figure 2.2:** Illustration of a transition from one state to another. (a) A simple transition. (b) A more complex set of transitions.

One type of transition, or _transient response,_ is illustrated in Figure 2.2a, where a system is in a homeostatic state, labeled as state $\text{#1}$, and is perturbed at time zero. Over some time period, as a result of the perturbation, it transitions into another homeostatic state (state $\text{#2}$). We are interested in characteristics such as the time duration of this response, as well as looking at the dynamic states that the network exhibits during this transition. Complex types of transitions are shown in Figure 2.2b.

It should be noted that when complex kinetic models are studied, there are two ways to perturb a system and induce a transient response. One is to instantaneously change the initial condition of one of the state variables (typically a concentration), and the second is to change the state of an environmental variable that represents an input to the system. The latter perturbation is the one that is biologically meaningful, whereas the former may be of some mathematical interest.

### Visualizing dynamic states
There are several ways to graphically represent dynamic states:

* First, we can represent them on a map (Figure 2.3a). If we have a reaction or a compound map for a network of interest, we can simply draw it out on a computer screen and leave open spaces above the arrows and the concentrations into which we can write numerical values for these quantities. These quantities can then be displayed dynamically as the simulation proceeds, or by a graph showing the changes in the variable over time. This representation requires writing complex software to make such an interface.

* A second, and probably more common, way of viewing dynamic states is to simply graph the state variables, $x$, as a function of time (Figure 2.3b). Such graphs show how the variables move up and down, and on which time scales. Often, one uses a logarithmic scale for the y-axis, and that often delineates the different time constants on which a variable moves.

* A third way to represent dynamic solutions is to plot two state variables against one another in a two-dimensional plot (Figure 2.3c). This representation is known as a _phase portrait_. Plotting two variables against one another traces out a curve in this plane along which time is a parameter. At the beginning of the trajectory, time is zero, and at the end, time has gone to infinity. These phase portraits will be discussed in more detail in Chapter 3.

**Figure 2.3:** Graphical representation of dynamic states.

## Primer on Rate Laws
The reaction rates, $v_i$, are described mathematically using kinetic theory. In this section, we will discuss some of the fundamental concepts of kinetic theory that lead to their formation.

### Elementary reactions
The fundamental events in chemical reaction networks are elementary reactions. There are two types of elemental reactions:

$$\begin{align} &\text{linear}  &x \stackrel{v}{\rightarrow} \tag{2.4a} \\ &\text{bi-linear}  & x_1 + x_2 \stackrel{v}{\rightarrow} \tag{2.4b} \end{align}$$

A special case of a bi-linear reaction is when $x_1$ is the same as $x_2$ in which case the reaction is second order.

Elementary reactions represent the irreducible events of chemical transformations, analogous to a base pair being the irreducible unit of DNA sequence. Note that rates, $v$, and concentrations, $x$, are non-negative variables, that is;

$$\begin{equation} x \geq 0, \ v \geq 0 \tag{2.5} \end{equation}$$

### Mass action kinetics
The fundamental assumption underlying the mathematical description of reaction rates is that they are proportional to the collision frequency of molecules taking part in a reaction. Most commonly, reactions are bi-linear, where two different molecules collide to produce a chemical transformation. The probability of a collision is proportional to the concentration of a chemical species in a 3-dimensional unconstrained domain. This proportionality leads to the elementary reaction rates:

$$\begin{align} \text{linear} \ \ &v = kx \ &\text{where the units on}& \ k \ \text{are time}^{-1} \ \text{and} \tag{2.6a} \\ \text{bi-linear} \ \ &v = kx_1x_2 \ &\text{where the units on}& \ k \ \text{are time}^{-1}\text{conc}^{-1} \tag{2.6b} \end{align}$$

### Enzymes increase the probability of the 'right' collision
Not all collisions of molecules have the same probability of producing a chemical reaction. Collisions at certain angles are more likely to produce a reaction than others. As illustrated in Figure 2.4, molecules bound to the surface of an enzyme can be oriented to produce collisions at certain angles, thus accelerating the reaction rate. The numerical values of the rate constants are thus genetically determined as the structure of a protein is encoded in the sequence of the DNA. Sequence variation in the underlying gene in a population leads to differences amongst the individuals that make up the population. Principles of enzyme catalysis are further discussed in Section 5.1.

**Figure 2.4:** A schematic showing how the binding sites of two molecules on an enzyme bring them together to collide at an optimal angle to produce a reaction. Panel A: Two molecules can collide at random and various angles in free solution. Only a fraction of the collisions lead to a chemical reaction. Panel B: Two molecules bound to the surface of a reaction can only collide at a highly restricted angle, substantially enhancing the probability of a chemical reaction between the two compounds. Redrawn based on (Lowenstein, 2000).

### Generalized mass action kinetics
The reaction rates may not be proportional to the concentration in certain circumstances, and we may have what are called _power-law kinetics_. The mathematical form of the elementary rate laws are

$$\begin{align} v &= kx^a  \tag{2.7a} \\ v &= kx_1^ax_2^b  \tag{2.7b} \end{align}$$

where $a$ and $b$ can be greater or smaller than unity. In cases where a restricted geometry reduces the probability of collision relative to a geometrically-unrestricted case, the numerical values of $a$ and $b$ are less than unity, and vice versa.

### Combining elementary reactions
In the analysis of chemical kinetics, the elementary reactions are often combined into reaction mechanisms. Following are two such examples:

#### Reversible reactions:
If a chemical conversion is thermodynamically reversible, then the two opposite reactions can be combined as

$$\begin{equation} x_1 \underset{v_{-}}{\stackrel{v_+}{\rightleftharpoons}} x_2 \end{equation}$$

The net rate of the reaction can then be described by the difference between the forward and reverse reactions;

$$\begin{align} v_{net} &= v^+ - v^- = k^+x_1 - k^-x_2, \tag{2.8a} \\ &K_{eq} = x_2 / x_1 = k^+/k^- \tag{2.8b} \end{align}$$

where $K_{eq}$ is the equilibrium constant for the reaction. Note that $v_{net}$ can be positive or negative. Both $k^+$ and $k^-$ have units of reciprocal time. They are thus inverses of time constants. Similarly, a net reversible bi-linear reaction can be written as

$$\begin{equation} x_1 + x_2 \underset{v_{-}}{\stackrel{v_+}{\rightleftharpoons}} x_3 \end{equation}$$

The net rate of the reaction can then be described by

$$\begin{align} v_{net} &= v^+ - v^- = k^+x_1x_2 - k^-x_3, \\ &K_{eq} = x_3 / x_1x_2 = k^+/k^- \end{align}$$

where $K_{eq}$ is the equilibrium constant for the reaction. The units on the rate constant $(k^+)$ for a bi-linear reaction are concentration per time. Note that we can also write this equation as

$$\begin{equation} v_{net} = k^+x_1x_2 - k^-x_3 = k^+(x_1x_2 - x_3/K_{eq}) \end{equation}$$

that can be a convenient form as often the $K_{eq}$ is a known number with a thermodynamic basis, and thus only a numerical value for $k^+$ needs to be estimated.

#### Converting enzymatic reaction mechanisms into rate laws:
Often, more complex combinations of elementary reactions are analyzed. The classical irreversible Michaelis-Menten mechanism is comprised of three elementary reactions.

$$\begin{equation} S + E \underset{v_{-1} = k_{-1}x}{\stackrel{v_1 = k_1se}{\rightleftharpoons}} X \stackrel{v_2 = k_2x}{\longrightarrow} E + P \end{equation}$$

where a substrate, $S$, binds to an enzyme to form a complex, $X$, that can break down to generate the product, $P$. The concentrations of the corresponding chemical species is denoted with the same lower case letter; i.e., $e=[E]$, etc. This reaction mechanism has two conservation quantities associated with it: one on the enzyme $e_{tot} = e + x$ and one on the substrate $s_{tot} = s+x+p$.

A quasi-steady-state assumption (QSSA), $dx/dt=0$, is then applied to generate the classical rate law

$$\begin{equation} \frac{ds}{dt} = \frac{-v_ms}{K_m + s} \tag{2.9} \end{equation}$$

that describes the kinetics of this reaction mechanism. This expression is the best-known rate equation in enzyme kinetics. It has two parameters: the maximal reaction rate $v_m$, and the Michaelis-Menten constant $K_m = (k_{-1} + k_2)/k_1$. The use and applicability of kinetic assumptions to deriving rate laws for enzymatic reaction mechanisms is discussed in detail in Chapter 5.

It should be noted that the elimination of the elementary rates through the use of the simplifying kinetic assumptions _fundamentally changes_ the mathematical nature of the dynamic description from that of bi-linear equations to that of hyperbolic equations (i.e., Eq. 2.9) and, more generally, to ratios of polynomial functions.

### Pseudo-first order rate constants (PERCs)
The effects of temperature, pH, enzyme concentrations, and other factors that influence the kinetics can often be accounted for in a condition specific numerical value of a constant that looks like a regular elementary rate constant, as in Eq (2.4). The advantage of having such constants is that it simplifies the network dynamic analysis. The disadvantage is that dynamic descriptions based on PERCs are condition specific. This issue is discussed in Parts 3 and 4 of the book.

### The mass action ratio ($\Gamma$)
The equilibrium relationship among reactants and products of a chemical reaction are familiar to the reader. For example, the equilibrium relationship for the PGI reaction (Eq. (2.8)) is

$$\begin{equation} K_{eq} = \frac{[\text{F6P}]_{eq}}{[\text{G6P}]_{eq}} \tag{2.10} \end{equation}$$

This relationship is observed in a closed system after the reaction is allowed to proceed to equilibrium over a long time, $t \rightarrow \infty$, (which in practice has a meaning relative to the time constant of the reaction, $t \gg \tau_f$).

However, in a cell, as shown in Eq. (2.2), the PGI reactions operate in an "open" environment, i.e., G6P is being produced and F6P is being consumed. The reaction reaches a steady state in a cell that will have concentration values that are different from the equilibrium value. The _mass action ratio_ for open systems, defined to be analogous to the equilibrium constant, is

$$\begin{equation} \Gamma = \frac{[\text{F6P}]_{ss}}{[\text{G6P}]_{ss}} \tag{2.11} \end{equation}$$

The mass action ratio is denoted by $\Gamma$ in the literature.

### 'Distance' from equilibrium
The numerical value of the ratio $\Gamma / K_{eq}$ relative to unity can be used as a measure of how far a reaction is from equilibrium in a cell. Fast reversible reactions tend to be close to equilibrium in an open system. For instance, the net reaction rate for a reversible bi-linear reaction (Eq. (2.2)) can be written as:

$$\begin{equation} v_{net} = k^+x_1x_2 - k^-x_3 = k^+x_1x_2(1 - \Gamma/K_{eq}) \end{equation}$$

If the reaction is "fast" then $(k^+x_1x_2)$ is a "large" number and thus $(1 - \Gamma/K_{eq})$ tends to be a "small" number, since the net reaction rate is balanced relative to other reactions in the network.

### Recap
These basic considerations of reaction rates and enzyme kinetic rate laws are described in much more detail in other standard sources, e.g., (Segal, 1975). In this text, we are not so concerned about the details of the mathematical form of the rate laws, but rather with the order-of-magnitude of the rate constants and how they influence the properties of the dynamic response.

## More on Aggregate Variables
Pools, or aggregate variables, form as a result of well-separated time constants. Such pools can form in a hierarchical fashion. Aggregate variables can be physiologically significant, such as the total inventory of high-energy phosphate bonds, or the total inventory of particular types of redox equivalents. These important concepts are perhaps best illustrated through a simple example that should be considered a primer on a rather important and intricate subject matter. Formation of aggregate variables in complex models is seen throughout Parts III and IV of this text.

**Figure 2.5:** The chemical transformations involved in the distribution of high-energy phosphate bonds among adenosines.

### Distribution of high-energy phosphate among the adenylate phosphates
In Figure 2.5 we show the skeleton structure of the transfer of high-energy phosphate bonds among the adenylates. In this figure we denote the use of ATP by $v_1$ and the synthesis of ATP from ADP by $v_2$, $v_5$ and $v_{-5}$ denote the reaction rates of adenylate kinase that distributes the high energy phosphate bonds among ATP, ADP, and AMP, through the reaction

$$\begin{equation} 2 \text{ADP} \leftrightharpoons \text{ATP} + \text{AMP} \tag{2.12} \end{equation}$$

Finally, the synthesis of AMP and its degradation is denoted by $v_3$ and $v_4$, respectively. The dynamic mass balance equations that describe this schema are:

$$\begin{align} \frac{d \text{ATP}}{dt} &= -v_1 + v_2 + v_{5, net} \tag{2.13a} \\ \frac{d \text{ADP}}{dt} &= v_1 - v_2 - 2 v_{5, net} \tag{2.13b} \\ \frac{d \text{AMP}}{dt} &= v_3 - v_4 + v_{5, net} \tag{2.13c} \end{align} $$

The responsiveness of these reactions falls into three categories: $v_{5, net} (=v_5 - v_{-5})$ is a _fast_ reversible reaction, $v_1$ and $v_2$ have _intermediate_ time scales, and the kinetics of $v_3$ and $v_4$ are _slow_ and have large time constants associated with them. Based on this time scale decomposition, we can combine the three concentrations so that they lead to the elimination of the reactions of a particular response time category on the right hand side of (Eq. 2.13). These combinations are as follows:

* First, we can eliminate all but the slow reactions by forming the sum of the adenosine phosphates.

    $$\begin{equation} \frac{d}{dt}(\text{ATP} + \text{ADP} + \text{AMP}) = v_3 - v_4\ \text{(slow)} \tag{2.14} \end{equation}$$

    The only reaction rates that appear on the right hand side of the equation are $v_3$ and $v_4$, that are the slowest reactions in the system. Thus, the summation of ATP, ADP, and AMP is a pool or aggregate variable that is expected to exhibit the slowest dynamics in the system.

* The second pooled variable of interest is the summation of 2ATP and ADP that represents the total number of high energy phosphate bonds found in the system at any given point in time:

    $$\begin{equation} \frac{d}{dt}(2 \text{ATP} + \text{ADP}) = -v_1 + v_2\ \text{(intermediate)} \tag{2.15} \end{equation}$$

    This aggregate variable is only moved by the reaction rates of intermediate response times, those of $v_1$ and $v_2$.

* The third aggregate variable we can form is the sum of the energy carrying nucleotides which are

    $$\begin{equation} \frac{d}{dt}(\text{ATP} + \text{ADP}) = -v_{5, net}\ \text{(fast)} \tag{2.16} \end{equation}$$

    This summation will be the fastest aggregate variable in the system.

Notice that by combining the concentrations in certain ways, we define aggregate variables that may move on distinct time scales in the simple model system, and, in addition, we can interpret these variables in terms of their metabolic physiological significance. However, in general, time scale decomposition is more complex as the concentrations that influence the rate laws may move on many time scales and the arguments in the rate law functions must be pooled as well.

### Using ratios of aggregate variables to describe metabolic physiology
We can define an aggregate variable that represents the _capacity_ to carry high-energy phosphate bonds. That simply is the summation of $\text{ATP} + \text{ADP} + \text{AMP}.$ This number multiplied by 2 would be the total number of high energy phosphate bonds that can be stored in this system. The second variable that we can define here would be the _occupancy_ of that capacity, $\textit{2ATP + ADP}$, which is simply an enumeration of how much of that capacity is occupied by high-energy phosphate bonds. Notice that the occupancy variable has a conjugate pair, which would be the vacancy variable. The ratio of these two aggregate variables forms a charge

$$\begin{equation} \text{charge} = \frac{\text{occupancy}}{\text{capacity}} \tag{2.17} \end{equation}$$

called the _energy charge,_ given by

$$\begin{equation} \text{E.C} = \frac{2 \text{ATP} \ + \ \text{ADP}}{2(\text{ATP} \ + \ \text{ADP} \ + \ \text{AMP})} \tag{2.18} \end{equation}$$

which is a variable that varies between 0 and 1. This quantity is the _energy charge_ defined by Daniel Atkinson (Atkinson, 1968). In cells, the typical numerical range for this variable when measured is 0.80-0.90.

In a similar way, one can define other redox charges. For instance, the _catabolic redox charge_ on the NADH carrier can be defined as

$$\begin{equation} \text{C.R.C} = \frac{\text{NADH}}{\text{NADH} \ + \ \text{NAD}} \tag{2.19} \end{equation}$$

which simply is the fraction of the NAD pool that is in the reduced form of NADH. It typically has a low numerical value in cells, i.e., about 0.001-0.0025, and therefore this pool is typically discharged by passing the redox potential to the electron transfer system (ETS). The _anabolic redox charge_

$$\begin{equation} \text{A.R.C} = \frac{\text{NADPH}}{\text{NADPH} \ + \ \text{NADP}} \tag{2.20} \end{equation}$$

in contrast, tends to be in the range of 0.5 or higher, and thus this pool is charged and ready to drive biosynthetic reactions. Therefore, pooling variables together based on a time scale hierarchy and chemical characteristics can lead to aggregate variables that are physiologically meaningful.

In Chapter 8  we further explore these fundamental concepts of time scale hierarchy. They are then used in Parts III and IV in interpreting the dynamic states of realistic biological networks.

## Time Scale Decomposition
### Reduction in dimensionality
As illustrated by the examples given in the previous section, most biochemical reaction networks are characterized by many time constants. Typically, these time constants are of very different orders of magnitude. The hierarchy of time constants can be represented by the time axis, Figure 2.6. Fast transients are characterized by the processes at the extreme left and slow transients at the extreme right. The process time scale, i.e., the time scale of interest, can be represented by a _window of observation_ on this time axis. Typically, we have three principal ranges of time constants of interest if we want to focus on a limited set of events taking place in a network. We can thus decompose the system response in time. To characterize network dynamics completely we would have to study all the time constants.

**Figure 2.6:** Schematic illustration of network transients that overlap with the time span of observation. n, n + 1, ... represent the decadic order of time constants.

### Three principal time constants
One can readily conceptualize this by looking at a three-dimensional linear system where the first time constant represents the fast motion, the second represents the time scale of interest, and the third is a slow motion, see Figure 2.7. The general solution to a three-dimensional linear system is

$$\begin{align} \textbf{x}(t) &=\textbf{v}_1  \langle \textbf{u}_1, \ \textbf{x}_0 \rangle \ \text{exp}(\lambda_1 t) && \text{fast} \\ &+\textbf{v}_2 \langle \textbf{u}_2, \ \textbf{x}_0 \rangle \ \text{exp}(\lambda_2 t) && \text{intermediate} \\ &+\textbf{v}_3 \langle \textbf{u}_3, \ \textbf{x}_0 \rangle \ \text{exp}(\lambda_3 t) && \text{slow} \tag{2.21} \end{align}$$

where $\textbf{v}_i$ are the _eigenvectors,_ $\textbf{u}_i$ are the _eigenrows,_ and $\boldsymbol{\lambda}_i$ are the _eigenvalues_ of the Jacobian matrix. The eigenvalues are negative reciprocals of time constants.

The terms that have time constants faster than the observed window can be eliminated from the dynamic description as these terms are small. However, the mechanisms which have transients slower than the observed time exhibit high "inertia" and hardly move from their initial state and can be considered constants.

**Figure 2.7:** A schematic of a decay comprised of three dynamic modes with well-separated time constants.

#### Example: 3D motion simplifying to a 2D motion
Figure 2.8 illustrates a three-dimensional space where there is rapid motion into a slow two-dimensional subspace. The motion in the slow subspace is spanned by two "slow" eigenvectors, whereas the fast motion is in the direction of the "fast" eigenvector.

**Figure 2.8:** Fast motion into a two-dimensional subspace.

### Multiple time scales
In reality there are many more than three time scales in a realistic network. In metabolic systems there are typically many time scales and a hierarchical formation of pools, Figure 2.9. The formation of such hierarchies will be discussed in Parts III and IV of the text.

**Figure 2.9:** Multiple time scales in a metabolic network and the process of pool formation. This figure represents human folate metabolism. (a) A map of the folate network. (b) An illustration progressive pool formation. Beyond the first time scale pools form between CHF and CH2F; and 5MTHF, 10FTHF, SAM; and MET and SAH (these are abbreviations for the long, full names of these metabolites). DHF and THF form a pool beyond the second time scale. Beyond the third time scale CH2F/CHF join the 5MTHF/10FTHF/SAM pool. Beyond the fourth time scale HCY joins the MET/SAH pool. Ultimately, on time scales on the order of a minute and slower, interactions between the pools of folate carriers and methionine metabolites interact. Courtesy of Neema Jamshidi (Jamshidi, 2008a).

## Network Structure versus Dynamics
The stoichiometric matrix represents the topological structure of the network, and this structure has significant implications with respect to what dynamic states a network can take. Its null spaces give us information about pathways and pools. It also determines the structural features of the gradient matrix. Network topology can have a dominant effect on network dynamics.

### The null spaces of the stoichiometric matrix
Any matrix has a right and a left null space. The right null space, normally called just the null space, is defined by all vectors that give zero when post-multiplying that matrix:

$$\begin{equation} \textbf{Sv}=0 \tag{2.22} \end{equation}$$

The null space thus contains all the steady state flux solutions for the network. The null space can be spanned by a set of basis vectors that are pathway vectors (SB1).

The left null space is defined by all vectors that give zero when pre-multiplying that matrix:

$$\begin{equation} \textbf{lS}=0 \tag{2.23} \end{equation}$$

These vectors $\textbf{l}$ correspond to pools that are always conserved at all time scales. We will call them _time invariants_. Throughout the book we will look at these properties of the stoichiometric matrices that describe the networks being studied.

**Figure 2.10:** A schematic showing how the structure of $\textbf{S}$ and $\textbf{G}$ form matrices that have non-zero elements in the same location if one of these matrices is transposed. The columns of $\textbf{S}$ and the rows of $\textbf{G}$ have similar but not identical vectors in an n-dimensional space. Note that this similarity only holds once the two opposing elementary reactions have been combined into a net reaction.

### The structure of the gradient matrix
We will now examine some of the properties of $\textbf{G}$. If a compound $x_i$ participates in reaction $v_j$, then the entry $s_{i,j}$ is non-zero. Thus, a net reaction

$$\begin{equation} x_i + x_{i + 1} \stackrel{v_j}{\leftrightharpoons} x_{i + 2} \tag{2.24} \end{equation}$$

with a net reaction rate

$$\begin{equation} v_j = v_j^+ - v_j^- \tag{2.25} \end{equation}$$

generates three non-zero entries in $\textbf{S}$: $s_{i,j}$, $s_{i + 1,j}$, and $s_{i + 2,j}$. Since compounds $x_i$, $x_{i + 1}$, and $x_{i + 2}$ influence reaction $v_j$, they will also generate non-zero elements in $\textbf{G}$, see Figure 2.10. Thus, non-zero elements generated by the reactions are:

$$\begin{equation} g_{j, i} = \frac{\partial v_j}{\partial x_i}, \ g_{j, i + 1} = \frac{\partial v_j}{\partial x_{i + 1}}, \ \text{and} \  g_{j, i + 2} = \frac{\partial v_j}{\partial x_{i + 2}} \tag{2.26} \end{equation}$$

In general, every reaction in a network is a reversible reaction. Hence we have the the following relationships between the elements of $\textbf{S}$ and $\textbf{G}$:

$$\begin{align} \text{if} \ &s_{i, j} = 0 \ \text{then} \ g_{j, i} = 0 \\ \text{if} \ &s_{i, j} \ne 0 \ \text{then} \ g_{j, i} \ne 0 \\ \text{if} \ &s_{i, j} > 0 \ \text{then} \ g_{j, i} < 0 \\ \text{if} \ &s_{i, j} < 0 \ \text{then} \ g_{j, i} >0 \end{align}$$

Note that for the rare cases where a reaction is effectively irreversible, an element in $\textbf{G}$ can become very small, but in principle finite.

It can thus be seen that

$$\begin{equation} -\textbf{G}^T \ \tilde \ \ \textbf{S} \tag{2.27} \end{equation}$$

in the sense that both will have non-zero elements in the same location. These elements will have opposite signs.

### Stoichiometric autocatalysis
The fundamental structure of most catabolic pathways in a cell is such that a compound is imported into a cell and then some property stored on cofactors is transferred to the compound and the molecule is thus "charged" with this property. This charged form is then degraded into a waste product that is secreted from the cell. During that degradation process, the property that the molecule was charged with is re-extracted from the compound, often in larger quantities than was used in the initial charging of the compound. This pathway structure is the cellular equivalent of "it takes money to make money," and its basic network structure is in Figure 2.11.

**Figure 2.11:** The prototypic pathway structure for degradation of a carbon substrate.

This figure illustrates the import of a substrate, $S$, to a cell. It is charged with high-energy phosphate bonds to form an intermediate, $X$. $X$ is then subsequently degraded to a waste product, $W$, that is secreted. In the degradation process, ATP is recouped in a larger quantity than was used in the charging process. This means that there is a net production of ATP in the two steps, and that difference can be used to drive various load functions on metabolism.

The consequence of this schema is basically _stoichiometric autocatalysis_ that can lead to multiple steady states. The rate of formation of $\text{ATP}$ from this schema as balanced by the load parameters is illustrated in Figure 2.12. This figure shows that the $\text{ATP}$ generation is 0 if all the adenosine phosphates are in the form of $\text{ATP}$ because there is no $\text{ADP}$ to drive the conversion of X to W. The $\text{ATP}$ generation is also 0 if there is no $\text{ATP}$ available, because $S$ cannot be charged to form $X$. The curve in between $\text{ATP} = 0$ and $\text{ATP} = \text{ATP}_{max}$ will be positive. The $\text{ATP}$ load, or use rate, will be a curve that grows with $\text{ATP}$ concentration and is sketched here as a hyperbolic function. As shown, there are three intersections in this curve, with the upper stable steady-state being the physiological state of this system. This system can thus have multiple steady-states and this property is a consequence of the topological structure of this reaction network.

### Network structure
The three topics discussed in this section show that the stoichiometric matrix has a dominant effect on integrated network functions and sets constraints on the dynamic states that a network can achieve. The numerical values of the elements of the gradient matrix determine which of these states are chosen.

## Physico-Chemical Effects
Molecules have other physico-chemical properties besides the collision rates that are used in kinetic theory. They also have osmotic properties and are electrically charged. Both of these features influence dynamic descriptions of biochemical reaction networks.

### The constant volume assumption
Most systems that we identify in systems biology correspond to some biological entity. Such entities may be an organelle like the nucleus or the mitochondria, or it may be the whole cell, as illustrated in Figure 2.13.

A compound, $x_i$, internal to the system, has a mass balance on the total amount per cell. We denote this quantity with an $M_i$. $M_i$ is a product of the volume per cell, $V$, and the concentration of the compound, $x_i$, which is amount per volume

$$\begin{equation} M_i = V \ x_i \tag{2.28} \end{equation}$$

The time derivative of the amount per cell is given by:

$$\begin{equation} \frac{M_i}{dt} = \frac{d}{dt}(V \ x_i) = V \frac{d x_i}{dt} + x_i \frac{dV}{dt} \tag{2.29} \end{equation}$$

**Figure 2.13:** An illustration of a 'system' with a defined boundary, inputs and outputs, and an internal network of reactions. The $V$ volume of the system may change over time. $\Pi$ denotes osmotic pressure, see (Eq. 2.32).

The time change of the amount $M_i$ per cell is thus dependent on two dynamic variables. One is $dx_i/dt$ which is the time change in the concentration of $x_i$, and the second is $dV/dt$ which is the change in volume with time. The volume is typically taken to be time invariant and therefore the term $dV/dt$ is equal to 0 and therefore results in a system that is of _constant volume_. In this case

$$\begin{equation} \frac{d x_i}{dt} = \frac{1}{V}\frac{d M_i}{dt} \tag{2.30} \end{equation}$$

This constant volume assumption (recall Table 1.2) needs to be carefully scrutinized when one builds kinetic models since volumes of cellular compartments tend to fluctuate and such fluctuations can be very important. Very few kinetic models in the current literature account for volume variation because it is mathematically challenging and numerically difficult to deal with. A few kinetic models have appeared, however, that do take volume fluctuations into account (Joshi, 1989m and Klipp, 2005).

### Osmotic balance
Molecules come with osmotic pressure, electrical charge, and other properties, all of which impact the dynamic states of networks. For instance, in cells that do not have rigid walls, the osmotic pressure has to be balanced inside $(\Pi_{in})$ and outside $(\Pi_{out})$ of the cell (Figure 2.13), i.e.,

$$\begin{equation} \Pi_{in} = \Pi_{out} \tag{2.31} \end{equation}$$

At first approximation, osmotic pressure is proportional to the total solute concentration,

$$\begin{equation} \Pi = R T \sum_i x_i \tag{2.32} \end{equation}$$

although some compounds are more osmotically-active than others and have osmotic coefficients that are not unity. The consequences are that if a reaction takes one molecule and splits it into two, the reaction comes with an increase in osmotic pressure that will impact the total solute concentration allowable inside the cell, as it needs to be balanced relative to that outside the cell. Osmotic balance equations are algebraic equations that are often complicated and therefore are often conveniently ignored in the formulation of a kinetic model.

### Electroneutrality
Another constraint on dynamic network models is the accounting for electrical charge. Molecules tend to be charged positively or negatively. Elementary charges cannot be separated, and therefore the total number of positive and negative charges within a compartment must balance. Any import and export in and out of a compartment of a charged species has to be counterbalanced by the equivalent number of molecules of the opposite charge crossing the membrane. Typically, bilipid membranes are impermeable to cations, but permeable to anions. For instance, the deliberate displacement of sodium and potassium by the ATP-driven sodium potassium pump is typically balanced by chloride ions migrating in and out of a cell or a compartment leading to a state of electroneutrality both inside and outside the cell. The equations that describe electroneutrality are basically a summation of the charge, $z_i$, of a molecule multiplied by its concentration,

$$\begin{equation} \sum_i z_ix_i = 0 \tag{2.33} \end{equation}$$

and such terms are summed up over all the species in a compartment. That sum has to add up to 0 to maintain electroneutrality. Since that summation includes concentrations of species, it represents an algebraic equation that is a constraint on the allowable concentration states of a network.

## Summary

* Time constants are key quantities in dynamic analysis. Large biochemical reaction networks typically have a broad spectrum of time constants.

* Well-separated time constants lead to pooling of variables to form aggregates. Aggregate variables represent a coarse-grained (i.e., lower dimensional) view of network dynamics and can lead to physiologically meaningful variables.

* Elementary reactions and mass action kinetics are the irreducible events in dynamic descriptions of networks. Elementary reactions are often combined into reaction mechanisms from which rate laws are derived using simplifying assumptions.

* Network structure has an overarching effect on network dynamics. Certain physico-chemical effects can as well. Thus topological analysis is useful, and so is a careful examination of the assumptions (recall Table 1.2) that underlie the dynamic mass balances (Eq. (1.1)) for the system being modeled and simulated.

$\tiny{\text{© B. Ø. Palsson 2011;}\ \text{This publication is in copyright.}\\ \text{Subject to statutory exception and to the provisions of relevant collective licensing agreements,}\\ \text{no reproduction of any part may take place without the written permission of Cambridge University Press.}}$


```R
options(warn=-1)
# load library
library(reshape2) # for melt and cast functions
library(ggplot2) # for plotting functions
library(tm) # text mining library
#install.packages("SnowballC")
library(SnowballC)
```

## Question 1

### 1. Derive expectation and maximation steps of hard-EM algorithm for document clustering

- N is the total number of documents, K is the number of clusters.
- {d1....dn} are the documents, with corresponding latent variables {z1...zn) where zn:=(zn1,....,znk) is the cluster assignment vector for the nth documents, znk = 1 if the document belongs to the cluster k and zero otherwise.
- Parameters: Phi k is the cluster proportion,and mu k is the word proportion. Where the sum of phi of all clusters equal to 1 and the sum of word proportion of all words in cluster k equal to 1.
\begin{equation}
\sum_{k=1}^{K} \varphi_{k}=1
\end{equation}

and

\begin{equation}
\sum_{w \in \mathcal{A}} \mu_{k, w}=1
\end{equation}

Then the probability of observed documents is given by:
\begin{equation}
\begin{aligned}
p\left(d_{1}, \ldots, d_{N}\right)=\prod_{n=1}^{N} p\left(d_{n}\right) &=\prod_{n=1}^{N} \sum_{k=1}^{K} p\left(z_{n, k}=1, d_{n}\right) \\
&=\prod_{n=1}^{N} \sum_{k=1}^{K}\left(\varphi_{k} \prod_{w \in \mathcal{A}} \mu_{k, w}^{c\left(w, d_{n}\right)}\right)
\end{aligned}
\end{equation}

Apply log to above, then the log-likelihood is:

\begin{equation}
\begin{aligned}
\ln p\left(d_{1}, \ldots, d_{N}\right)=\sum_{n=1}^{N} \ln p\left(d_{n}\right) &=\sum_{n=1}^{N} \ln \sum_{k=1}^{K} p\left(z_{n, k}=1, d_{n}\right) \\
&=\sum_{n=1}^{N} \ln \sum_{k=1}^{K}\left(\varphi_{k} \prod_{w \in \mathcal{A}} \mu_{k, w}^{c\left(w, d_{n}\right)}\right)
\end{aligned}
\end{equation}

To maximise the Likelihood of incomplete Data, we use EM algorithm.

First, as the parameters are unknown, we initialize the starting values of parameters θ. These values will be called θold, and the unknown parameters we want to estimate will be θnew.

\begin{equation}
\theta^{\text {old }}=\left(\boldsymbol{\varphi}^{\text {old }}, \boldsymbol{\mu}_{1}^{\text {old }}, \ldots, \boldsymbol{\mu}_{K}^{\text {old }}\right)
\end{equation}

Define Q function:

\begin{equation}
\begin{aligned}
Q\left(\boldsymbol{\theta}, \boldsymbol{\theta}^{\text {old }}\right) &:=\sum_{n=1}^{N} \sum_{k=1}^{K} p\left(z_{n, k}=1 \mid d_{n}, \boldsymbol{\theta}^{\text {old }}\right) \ln p\left(z_{n, k}=1, d_{n} \mid \boldsymbol{\theta}\right) \\
&=\sum_{n=1}^{N} \sum_{k=1}^{K} p\left(z_{n, k}=1 \mid d_{n}, \boldsymbol{\theta}^{\text {old }}\right)\left(\ln \varphi_{k}+\sum_{w \in \mathcal{A}} c\left(w, d_{n}\right) \ln \mu_{k, w}\right) \\
&=\sum_{n=1}^{N} \sum_{k=1}^{K} \gamma\left(z_{n, k}\right)\left(\ln \varphi_{k}+\sum_{w \in \mathcal{A}} c\left(w, d_{n}\right) \ln \mu_{k, w}\right)
\end{aligned}
\end{equation}

where

\begin{equation}
\gamma\left(z_{n, k}\right) = p\left(z_{n, k}=1 \mid d_{n}, \boldsymbol{\theta}^{\text {old }}\right)
\end{equation}

are the responsability factors

E step:
1. calculate γ(znk) based on estimated parameters
\begin{equation}
\gamma\left(z_{n k}\right):=p\left(z_{n k}=1 \mid \boldsymbol{d}_{n}, \boldsymbol{\theta}^{\text {old }}\right)
\end{equation}

2. for each document, find the cluster with the maximum probability.

\begin{equation}
Z^{*}=\operatorname{argmax}_{z} \gamma\left(z_{n, k}\right)=\operatorname{argmax}_{z} p\left(z_{n, k}=1 \mid d_{n}, \theta^{\text {old }}\right)
\end{equation}

M step:

For hard EM, there is no expectation on the latent variables, so :

\begin{equation}
\mathcal{Q}\left(\theta, \theta^{\text {old }}\right)=\sum_{n=1}^{N} \ln p\left(z_{n, k=Z^{*}}=1, d_{n} \mid \theta\right)
\end{equation}

Find:
\begin{equation}
\operatorname{argmax}_{\theta} \sum_{n=1}^{N}\left(\ln \varphi_{k=Z^{*}}+\sum_{w \in \mathcal{A}} c\left(w, d_{n}\right) \ln \mu_{k=Z^{*}, w}\right)
\end{equation}

1. Sub the z* calculated into the partial derivatives below and recalculate the estimations of the parametors, update the parameters.

\begin{equation}
\varphi_{k}=\frac{N_{k}}{N} \text { where } N_{k}:=\sum_{n=1}^{N} \gamma\left(z_{n, k}\right)
\end{equation}

and

\begin{equation}
\mu_{k, w}=\frac{\sum_{n=1}^{\prime} \gamma\left(z_{n, k}\right) c\left(w, d_{n}\right)}{\sum_{w^{\prime} \in \mathcal{A}} \sum_{n=1}^{N} \gamma\left(z_{n, k}\right) c\left(w^{\prime}, d_{n}\right)}
\end{equation}

Use \begin{equation}
\boldsymbol{\theta}^{\text {old }} \leftarrow \boldsymbol{\theta}^{\text {new }}
\end{equation} and repeat until converge

### 2. Implement the hard-EM and soft-EM

```R
# Initialize parameters (theta_old function)
theta <- function(size, K, seed = 123456){
    set.seed(seed)     # set seed
    phi.hat  <- matrix(1/K,nrow = K, ncol=1) # assume all clusters have the same size (we will update this later on)
    mu.hat <- matrix(runif(K*size),nrow = K, ncol = size)    # initiate Mu
    mu.hat <- prop.table(mu.hat, margin = 1)   # normalization to ensure that sum of each row is 1

    return (list("phi.hat" = phi.hat, "mu.hat" = mu.hat))
}

# Helper Function
# This function is needed to prevent numerical overflow/underflow when working with small numbers
logSum <- function(v) {
   m = max(v)
   return ( m + log(sum(exp(v-m))))
}
```

```R
# train objective function
train_obj <- function(theta_old, wf) {
  N <- dim(wf)[2] # number of documents
  K <- dim(theta_old$mu.hat)[1] # number of cluster

  nloglike = 0
  for (n in 1:N){
    lprob <- matrix(0,ncol = 1, nrow=K)
    for (k in 1:K){
      lprob[k,1] = sum(wf[,n] * log(theta_old$mu.hat[k,]))
    }
    nloglike <- nloglike - logSum(lprob + log(theta_old$phi))
  }

  return (nloglike)
}
```

```R
# EM function for document clustering(hard & soft)
EM.step <- function(wf, K = 4, max.epoch=10, soft = TRUE, seed){

    # Parameters Setting
    N <- ncol(wf) # number of documents
    W <- nrow(wf)   # number of words i.e. vocabulary size
    theta_old = theta(W, K, seed = seed) # initialize parameters
    gamma <- matrix(,nrow=N, ncol=K)  # empty posterior matrix

    # check initial values
    print(train_obj(theta_old,wf))
    # EM-step
    for(epoch in 1:max.epoch){

        # E step:
        for (n in 1:N){
            for (k in 1:K){
                ## calculate the posterior based on the estimated mu and rho in the "log space"
                gamma[n,k] <- log(theta_old$phi.hat[k]) +  sum(wf[,n] * log(theta_old$mu.hat[k,]))
            }
            # normalisation to sum to 1 in the log space
            logZ = logSum(gamma[n,])
            gamma[n,] = gamma[n,] - logZ
        }

        # converting back from the log space
        gamma <- exp(gamma)

        # for hard EM, we want the k with the highest probability to be 1
        if(soft == FALSE){
            # hard assignments:
            max.prob <- gamma==apply(gamma, 1, max) # for each point find the cluster with the maximum (estimated) probability
            gamma[max.prob] <- 1 # assign each point to the cluster with the highest probability
            gamma[!max.prob] <- 0 # remove points from clusters with lower probabilites
        }

        # M step:
        # we need this matrix (same shape as the ) here because when calculating mean,
        # it can result in zero which leads to log calculation result in NaN, to avoid this issue, add a small number to avoid 0
        eps = matrix(1e-10, nrow = W, ncol = K)
        for (k in 1:K){
            ## recalculate the estimations:
            theta_old$phi.hat[k] <- sum(gamma[,k])/N  # the cluster size
            theta_old$mu.hat[k,] <- ((wf%*%gamma[,k])+eps[,k])/sum((wf%*%gamma[,k])+eps[,k]) # new means (cluster cenroids)
            }
        # evaluate and compare likelihood
        print(train_obj(theta_old,wf))
    }

    # keep the final parameters and gamma (posterior matrix)
    return(list("theta"= theta_old,"posterior"=gamma))
}
```

### 3. run soft-EM and hard-EM on provided data with K = 4

```R
## read the file (each line of the text file is one document)
text <- readLines('./Task2A.txt')

## the terms before '\t' are the lables (the newsgroup names) and all the remaining text after '\t' are the actual documents
docs <- strsplit(text, '\t')
rm(text) # just free some memory!

# store the labels for evaluation
labels <- unlist(lapply(docs, function(x) x[1]))

# store the unlabeled texts
docs <- data.frame(unlist(lapply(docs, function(x) x[2])))
```

```R
# preprocessing
docs$doc_id <- rownames(docs)
colnames(docs) <- c("text","doc_id")

# create a corpus
docs <- DataframeSource(docs)
corp <- Corpus(docs)

# Preprocessing:
corp <- tm_map(corp, removeWords, stopwords("english")) # remove stop words
#(the most common word in a language that can be find in any document)
corp <- tm_map(corp, removePunctuation) # remove punctuation
corp <- tm_map(corp, stemDocument) # perform stemming (reducing inflected and derived words to their root form)
corp <- tm_map(corp, removeNumbers) # remove all numbers
corp <- tm_map(corp, stripWhitespace) # remove redundant spaces

# Create a matrix which its rows are the documents and colomns are the words.
# Each number in Document Term Matrix shows the frequency of a word (colomn header) in a particular document (row title)
dtm <- DocumentTermMatrix(corp)
# reduce the sparcity of out dtm
dtm <- removeSparseTerms(dtm, 0.90)

# store word frequency into a matrix
wf <- t(as.matrix(dtm))
```

```R
# run soft and hard EM
EM_soft <- EM.step(wf, K=4, max.epoch=15,soft = TRUE, seed = 123456)
EM_hard <- EM.step(wf, K=4, max.epoch=15,soft = FALSE, seed = 123456)
```

    [1] 459718.3
    [1] 425849.8
    [1] 422287.2
    [1] 420268.8
    [1] 419696.7
    [1] 419511.3
    [1] 419432.7
    [1] 419403
    [1] 419385.8
    [1] 419303.3
    [1] 419291.5
    [1] 419283
    [1] 419274.8
    [1] 419248.1
    [1] 419226.4
    [1] 419218.6
    [1] 459718.3
    [1] 425717.5
    [1] 422187.1
    [1] 420212.3
    [1] 419695.3
    [1] 419536.1
    [1] 419450
    [1] 419417.2
    [1] 419402
    [1] 419394.1
    [1] 419387.3
    [1] 419383.3
    [1] 419382.4
    [1] 419382.5
    [1] 419382.5
    [1] 419382.5

### 4. Perform PCA on clustering

```R
##--- Cluster Visualization -------------------------------------------------
cluster.viz <- function(counts, color.vector, title=' '){
    # PCA
    p.comp <- prcomp(counts, scale. = TRUE, center = TRUE)
    # visualize
    plot(p.comp$x, col=color.vector, pch=1,  main=title)
}
```

```R
# visualization settings
options(repr.plot.width=18, repr.plot.height=10)
par(mfrow=c(1,2))

# Get the color.vector(labels)
label.soft  <- apply(EM_soft$posterior, 1, which.max)
label.hard  <- apply(EM_hard$posterior, 1, which.max)

# normalize the count matrix for better visualization
counts <- scale(wf)
counts[is.nan(counts)] <- 0

# visualize the clusters estimated by soft and hard EM
cluster.viz(t(counts), label.soft, 'Estimated Clusters (Soft EM) - normalized')
cluster.viz(t(counts), label.hard, 'Estimated Clusters (Hard EM) - normalized')
cluster.viz(t(wf), label.soft, 'Estimated Clusters (Soft EM)')
cluster.viz(t(wf), label.hard, 'Estimated Clusters (Hard EM)')
# visualize the real clusters
cluster.viz(t(counts), factor(labels), 'Real Clusters - normalized')
cluster.viz(t(wf), factor(labels), 'Real Clusters')
```

Machine Learning study notes/Latent Variable Models and Neural Networks/src/31436285_assessment_2_q1.ipynb


 [mlcourse.ai](https://mlcourse.ai) - Open Machine Learning Course

Auteur: [Alexey Natekin](https://www.linkedin.com/in/natekin/), fondateur d’OpenDataScience, Machine Learning Evangelist. Traduit et édité par [Olga Daykhovskaya](https://www.linkedin.com/in/odaykhovskaya/), [Anastasia Manokhina](https://www.linkedin.com/in/anastasiamanokhina/), [Egor Polusmak](https://www.linkedin.com/in/egor-polusmak/), [Yuanyuan Pao](https://www.linkedin.com/in/yuanyuanpao/) et [Ousmane Cissé](https://github.com/oussou-dev). Ce matériel est soumis aux termes et conditions de la licence [Creative Commons CC BY-NC-SA 4.0](https://creativecommons.org/licenses/by-nc-sa/4.0/). L'utilisation gratuite est autorisée à des fins non commerciales.

 Thème 10. Gradient Boosting

Jusqu'à présent, nous avons couvert 9 sujets allant de l'analyse exploratoire de données à l'analyse de séries chronologiques en Python:
<spoiler title="List of the series' articles">
1. [Analyse exploratoire de données avec Pandas](https://medium.com/open-machine-learning-course/open-machine-learning-course-topic-1-exploratory-data-analysis-with-pandas-de57880f1a68)
2. [Visualisation de données avec Python](https://medium.com/open-machine-learning-course/open-machine-learning-course-topic-2-visual-data-analysis-in-python-846b989675cd)
3. [Classification, arbres-de-décision et k plus proches voisins](https://medium.com/open-machine-learning-course/open-machine-learning-course-topic-3-classification-decision-trees-and-k-nearest-neighbors-8613c6b6d2cd)
4. [Classification linéaire et régression](https://medium.com/open-machine-learning-course/open-machine-learning-course-topic-4-linear-classification-and-regression-44a41b9b5220)
5. [Bagging et random forest](https://medium.com/open-machine-learning-course/open-machine-learning-course-topic-5-ensemble-of-algorithms-and-random-forest-8e05246cbba7)
6. [Feature Engineering and Feature Selection](https://medium.com/open-machine-learning-course/open-machine-learning-course-topic-6-feature-engineering-and-feature-selection-8b94f870706a)
7. [Apprentissage non supervisé : (ACP) Analyse en Composantes Principales et Clustering](https://medium.com/open-machine-learning-course/open-machine-learning-course-topic-7-unsupervised-learning-pca-and-clustering -db7879568417)
8. [Vowpal Wabbit : Learning with Gigabytes of Data](https://medium.com/open-machine-learning-course/open-machine-learning-course-topic-8-vowpal-wabbit-fast-learning-with-gigabytes-of-data-60f750086237)
9. [Analyse des séries temporelles en Python](https://medium.com/open-machine-learning-course/open-machine-learning-course-topic-9-time-series-analysis-in-python-a270cb05e0b3)

Aujourd'hui, nous allons examiner l'un des algorithmes d'apprentissage automatique les plus populaires et les plus pratiques : le Gradient Boosting.

 Sommaire de l'article

Nous vous recommandons de lire cet article dans l’ordre décrit ci-dessous, mais n'hésitez pas à parcourir les différentes sections.

1. Introduction et histoire du Boosting

Histoire de la GBM

2. L'algorithme GBM

Problème

Descente de gradient fonctionnel

L'algorithme GBM classique de Friedman

Exemple pas à pas: comment fonctionne la GBM

3. Fonctions de perte

Fonctions de perte liées à la  régression

Fonctions de perte de classification

Poids

4. Conclusion

5. Mission de démonstration

6. Ressources utiles

# 1. Introduction et histoire du Boosting
Presque tout le monde en dans le domaine de l'apprentissage automatique a entendu parler du Gradient boosting. Nombreux data scientist incluent cet algorithme dans leur boîte à outils en raison des bons résultats obtenus sur tout problème (inconnu) donné.

De plus, XGBoost est souvent la recette standard pour [gagner](https://github.com/dmlc/xgboost/blob/master/demo/README.md#usecases) les cmpétitions de [ML](http://blog.kaggle.com/tag/xgboost/). Il est si populaire que l’idée de superposer des XGBoosts est devenue un mème. De plus, le boosting est un composant important dans [de nombreux systèmes de recommandation](https://en.wikipedia.org/wiki/Learning_to_rank#Practical_usage_by_search_engines); parfois, il est même considérée comme une [marque](https://yandex.com/company/technologies/matrixnet/).
Penchons-nous sur l'histoire et le développement du boosting.

Le boosting est né de [la question :](http://www.cis.upenn.edu/~mkearns/papers/boostnote.pdf) est-il possible d'obtenir un modèle fort à partir d'un grand nombre de modèles relativement faibles et simples ? Par «modèles faibles», nous n'entendons pas de simples modèles de base tels que les arbres de décision, mais des modèles avec des performances de précision médiocres, où médiocre est un peu meilleur que le hasard.

[Une réponse mathématique positive](http://www.cs.princeton.edu/~schapire/papers/strengthofweak.pdf) a été identifiée, mais il a fallu quelques années pour développer des algorithmes pleinement fonctionnels basés sur cette solution, par exemple AdaBoost. Ces algorithmes adoptent une approche dite "gourmande" : ils construisent d’abord une combinaison linéaire de modèles simples (algorithmes de base) en repesant les données d’entrée. Ensuite, le modèle (généralement un arbre de décision) est construit sur des objets précédemment prédits de manière incorrecte, auxquels des pondérations plus importantes ont été attribuées.

<spoiler title="More about AdaBoost">
De nombreux cours d'apprentissage automatique étudient AdaBoost - l'ancêtre du GBM (Gradient Boosting Machine). Cependant, depuis la fusion d'AdaBoost avec GBM, il est devenu évident qu'AdaBoost n'est qu'une variante particulière de GBM.

L'algorithme lui-même a une interprétation visuelle très claire et une intuition permettant de définir des poids. Jetons un coup d'œil au problème de classification des jouets suivant dans lequel nous allons scinder les données entre les arbres de profondeur 1 (également appelés «stumps») à chaque itération d'AdaBoost. Pour les deux premières itérations, nous avons l'image suivante :

La taille du point correspond à son poids, attribué à une prédiction incorrecte. À chaque itération, nous pouvons constater que ces poids augmentent - les "stumps" ne peuvent pas faire face à ce problème. Cependant, si nous votons (de manière pondérée) pour les "stumps", nous obtiendrons les bonnes classifications :

Pseudocode:
- Initialiser les poids des échantillons $\Large w_i^{(0)} = \frac{1}{l}, i = 1, \dots, l$.
- pour tout $t = 1, \dots, T$
    * Entraînez l'algo $\Large b_t$, laissez $\epsilon_t$ être l’erreur d’entraînement.
    * $\Large \alpha_t = \frac{1}{2}ln\frac{1 - \epsilon_t}{\epsilon_t}$.
    * Mettez à jour les poids des échantillons : $\Large w_i^{(t)} = w_i^{(t-1)} e^{-\alpha_t y_i b_t(x_i)}, i = 1, \dots, l$.
    * Normaliser les poids des échantillons: $\Large w_0^{(t)} = \sum_{j = 1}^k w_j^{(t)}, w_i^{(t)} = \frac{w_i^{(t)}}{w_0^{(t)}}, i = 1, \dots, l$.
- Retourne $\sum_t^{T}\alpha_tb_t$

[Voici](https://www.youtube.com/watch?v=k4G2VCuOMMg) un exemple plus détaillé d'AdaBoost où, en itérant, nous pouvons voir que les poids augmentent, en particulier à la frontière entre les classes.

AdaBoost fonctionne bien, mais [le manque](https://www.cs.princeton.edu/courses/archive/spring07/cos424/papers/boosting-survey.pdf) d'explication de la raison de la réussite de l'algorithme a semé le doute. Certains considéraient cela comme un super-algorithme, une solution miracle, mais d'autres étaient sceptiques et pensaient qu'AdaBoost était juste trop bien sur-appris

Le problème de sur-apprentissage existait bel et bien, surtout lorsque les données présentaient de fortes valeurs aberrantes. Par conséquent, dans ce type de problème, AdaBoost était instable. Heureusement, quelques professeurs du département de statistique de Stanford, qui avaient créé Lasso, Elastic Net et Random Forest, ont commencé à étudier l'algorithme. En 1999, Jerome Friedman a proposé la généralisation du développement d’algorithmes de Boosting - Gradient Boosting (Machine), également connu sous le nom de GBM. Avec ce travail, Friedman a mis en place la base statistique de nombreux algorithmes fournissant l'approche générale du Boosting pour l'optimisation dans l'espace fonctionnel.

CART, bootstrap, et beaucoup d'autres algorithmes sont issus du département des statistiques de Stanford. Ce faisant, leurs noms seront introduits dans les prochains manuels. Ces algorithmes sont très pratiques et certains travaux récents ne sont pas encore largement adoptés. Par exemple, consultez [glinternet](https://arxiv.org/abs/1308.2719).

Pas beaucoup d'enregistrements vidéo de Friedman sont disponibles. Bien qu'il y ait une très intéressante [interview](https://www.youtube.com/watch?v=8hupHmBVvb0) avec lui sur la création de CART et sur la façon dont ils ont résolu les problèmes de statistiques (similaires à la data analysis et à la data science aujourd'hui) il y a plus de 40 ans.

Il existe également une excellente [conférence](https://www.youtube.com/watch?v=zBk3PK3g-Fc) de Hastie, une rétrospective sur l'analyse de données fournie par l'un des créateurs des méthodes que nous utilisons tous les jours.

En général, la recherche en ingénierie et en algorithmes est passée d'une approche à part entière à la construction et à l'étude d'algorithmes. D'un point de vue mathématique, il ne s'agit pas d'un gros changement : nous ajoutons (ou renforçons) des algorithmes faibles et élargissons notre ensemble avec des améliorations progressives pour les parties des données où le modèle était inexact. Mais, cette fois, le prochain modèle simple ne repose pas uniquement sur des objets repondérés, mais améliore son approximation du gradient de la fonction objective globale. Ce concept ouvre grandement nos algorithmes à l'imagination et aux extensions.

## Histoire de la GBM

Plus de 10 ans après l’introduction de la GBM, celle-ci est devenue un élément essentiel de la boîte à outils de la science des données.
GBM a été étendu pour s’appliquer à différents problèmes statistiques : GLMboost et GAMboost pour renforcer les modèles GAM existants, CoxBoost pour les courbes de survie et RankBoost et LambdaMART pour le classement.
De nombreuses réalisations de GBM sont également apparues sous différents noms et sur différentes plateformes : GBM stochastique, GBDT (arbres de décision boostés par gradient), GBRT (arbres de régression boostés par gradient), MART (arbres de régression additive multiples), etc. En outre, la communauté du ML (Machine Learning) était très segmentée et dissociée, ce qui rendait difficile de savoir à quel point la stimulation était devenue généralisée.

Dans le même temps, le boosting avait été activement utilisé dans le classement des recherches. Ce problème a été réécrit en termes de fonction de perte qui pénalise les erreurs dans l'ordre de sortie. Il est donc devenu pratique de l'insérer simplement dans la GBM. AltaVista a été l’une des premières entreprises à introduire le renforcement du classement. Bientôt, les idées se répandent dans Yahoo, Yandex, Bing, etc. Une fois que cela s'est produit, le boosting est devenu l'un des principaux algorithmes utilisés non seulement dans la recherche, mais également dans les technologies de base de l'industrie.

Les compétitions  ML, en particulier Kaggle, ont joué un rôle majeur dans la popularisation du Boosting. À présent, les chercheurs disposaient d’une plate-forme commune leur permettant de faire face à différents problèmes de science des données avec un grand nombre de participants du monde entier. Avec Kaggle, il est possible de tester de nouveaux algorithmes sur les données réelles, ce qui permet aux algorithmes de "briller", et de fournir des informations complètes sur le partage des résultats de performance de modèle entre jeux de données de compétition. C’est exactement ce qui est arrivé avec le Boosting quand il a été utilisé chez [Kaggle](http://blog.kaggle.com/2011/12/21/score-xavier-conort-on-coming-second-in-give-me-some-credit/) (voir les entretiens avec les gagnants de Kaggle à partir de 2011 qui ont principalement utilisé le boosting). La bibliothèque [XGBoost](https://github.com/dmlc/xgboost) a rapidement gagné en popularité après son apparition. XGBoost n'est pas un nouvel algorithme unique. il s'agit simplement d'une réalisation extrêmement efficace de la GBM classique avec des heuristiques supplémentaires.

Cet algorithme a suivi le chemin très typique des algorithmes ML aujourd'hui : problème mathématique et savoir-faire algorithmique pour des applications pratiques réussies et une adoption en masse des années après sa première apparition.

# 2. L'algorithme GBM
### Problème

Nous allons résoudre le problème de l'approximation des fonctions dans un contexte d'apprentissage supervisé général. Nous avons un ensemble de fonctionnalités $ \large x $ et les variables cibles $\large y, \large \left\{ (x_i, y_i) \right\}_{i=1, \ldots,n}$ que nous utilisons pour restaurer la dépendance $\large y = f(x) $. Nous restaurons la dépendance en approximant $ \large \hat{f}(x) $ et en comprenant quelle approximation est la meilleure lorsque nous utilisons la fonction de perte $ \large L(y,f) $, que nous voulons minimiser: $ \large y \approx \hat{f}(x), \large \hat{f}(x) = \underset{f(x)}{\arg\min} \ L(y,f(x)) $.

Pour le moment, nous ne faisons aucune hypothèse concernant le type de dépendance $ \large f(x) $, le modèle de notre approximation $ \large \hat{f}(x) $ ou la distribution de la variable cible $ \large y $. Nous nous attendons seulement à ce que la fonction $ \large L(y,f) $ soit différentiable. Notre approche est très générale : nous définissons $ \large \hat {f}(x) $ en minimisant la perte :
$$ \large  \hat{f}(x) = \underset{f(x)}{\arg\min} \ \mathbb {E} _{x,y}[L(y,f(x))]  $$

Malheureusement, le nombre de fonctions $ \large f(x) $ n'est pas simplement grand, mais son espace fonctionnel est infiniment dimensionnel. C'est pourquoi il est acceptable pour nous de limiter l'espace de recherche par une famille de fonctions $ \large f(x, \theta), \theta \in \mathbb{R}^d $. Cela simplifie beaucoup l'objectif car nous avons maintenant une optimisation solvable des valeurs de paramètres:
$ \large \hat{f}(x) = f(x, \hat{\theta}),$
$$\large \hat{\theta} = \underset{\theta}{\arg\min} \ \mathbb {E} _{x,y}[L(y,f(x,\theta))] $$

Des solutions analytiques simples pour trouver les paramètres optimaux $ \large \hat{\theta} $ n'existant souvent pas, les paramètres sont généralement approximés de manière itérative. Pour commencer, nous écrivons la fonction de perte empirique $ \large L_{\theta}(\hat{\theta}) $ qui nous permettra d’évaluer nos paramètres en utilisant nos données. De plus, écrivons notre approximation $ \large \hat{\theta} $ pour un certain nombre d'itérations $ \large M $ sous forme de somme:
$ \large \hat{\theta} = \sum_{i = 1}^M \hat{\theta_i}, \\
\large L_{\theta}(\hat{\theta}) =  \sum_{i = 1}^N L(y_i,f(x_i, \hat{\theta}))$

Ensuite, il ne reste plus qu'à trouver un algorithme itératif approprié pour minimiser $\large L_{\theta}(\hat{\theta})$. La descente de gradient est l'option la plus simple et la plus fréquemment utilisée. Nous définissons le gradient comme étant $\large \nabla L_{\theta}(\hat{\theta})$ et y ajoutons nos évaluations itératives $\large \hat{\theta_i}$ (puisque nous minimisons la perte, nous ajoutons le signe moins). Notre dernière étape consiste à initialiser notre première approximation $\large \hat{\theta_0}$ et à choisir le nombre d'itérations $\large M$. Passons en revue les étapes de cet algorithme inefficace et naïf pour approximer $\large \hat{\theta}$:

1. Définir l'approximation initiale des paramètres $\large \hat{\theta} = \hat{\theta_0}$
2. Pour chaque itération $\large t = 1, \dots, M$, répétez les étapes 3 à 7:
3. Calculer le gradient de la fonction de perte $\large \nabla L_{\theta}(\hat{\theta})$ pour l'approximation courante $\large \hat{\theta}$
$\large \nabla L_{\theta}(\hat{\theta}) = \left[\frac{\partial L(y, f(x, \theta))}{\partial \theta}\right]_{\theta = \hat{\theta}}$
4. Définir l'approximation itérative actuelle $\large \hat{\theta_t}$ en fonction du gradient calculé
$\large \hat{\theta_t} \leftarrow −\nabla L_{\theta}(\hat{\theta})$
5. Mettre à jour l'approximation des paramètres $\large \hat{\theta}$:
$\large \hat{\theta} \leftarrow \hat{\theta} + \hat{\theta_t} = \sum_{i = 0}^t \hat{\theta_i} $
6. Enregistrer le résultat de l'approximation $\large \hat{\theta}$:
$\large \hat{\theta} = \sum_{i = 0}^M \hat{\theta_i} $
7. Utiliser la fonction trouvée $\large \hat{f}(x) = f(x, \hat{\theta})$

### Descente de gradient fonctionnel

Imaginons une seconde que nous puissions améliorer l'optimisation dans l'espace des fonctions et rechercher de manière itérative les approximations $\large \hat{f}(x)$ en tant que fonctions elles-mêmes. Nous allons exprimer notre approximation comme une somme d'améliorations incrémentales, chacune étant une fonction. Pour plus de commodité, nous commencerons immédiatement par la somme de l'approximation initiale $\large \hat{f_0}(x)$:
$$\large \hat{f}(x) = \sum_{i = 0}^M \hat{f_i}(x)$$

Rien n'est encore arrivé. nous avons seulement décidé de chercher notre approximation $\large \hat{f}(x)$ non pas comme un grand modèle avec beaucoup de paramètres (par exemple, un réseau de neurones), mais comme une somme de fonctions prétendant que nous nous déplaçons dans un espace fonctionnel.

Afin d'accomplir cette tâche, nous devons limiter notre recherche à une famille de fonctions $\large \hat{f}(x) = h(x, \theta)$. Il y a quelques problèmes ici - tout d’abord, la somme des modèles peut être plus compliquée que n’importe quel modèle de cette famille; Deuxièmement, l'objectif général est toujours dans l'espace fonctionnel. Notons qu'à chaque étape, nous devrons choisir un coefficient optimal $\large \rho \in \mathbb{R}$. Pour l'étape $\large t$, le problème est le suivant:
$$\large \hat{f}(x) = \sum_{i = 0}^{t-1} \hat{f_i}(x), \\
\large (\rho_t,\theta_t) = \underset{\rho,\theta}{\arg\min} \ \mathbb {E} _{x,y}[L(y,\hat{f}(x) +  \rho \cdot h(x, \theta))], \\
\large \hat{f_t}(x) = \rho_t \cdot h(x, \theta_t)$$

C'est ici que la magie opère. Nous avons défini tous nos objectifs en termes généraux, comme si nous pouvions former tout type de modèle $\large h(x, \theta)$ pour n’importe quel type de fonction de perte $\large L(y, f(x, \theta))$. En pratique, c'est extrêmement difficile, mais heureusement, il existe un moyen simple de résoudre ce problème.

Connaissant l'expression du gradient de la fonction de perte, nous pouvons calculer sa valeur sur nos données. Donc, entraînons les modèles de telle sorte que nos prédictions soient davantage corrélées à ce gradient (avec un signe moins). En d'autres termes, nous allons utiliser les moindres carrés pour corriger les prédictions avec ces résidus. Pour les tâches de classification, de régression et de classement, nous minimiserons la différence quadratique entre les pseudo-résidus $\large r$ et nos prédictions. Pour l'étape $\large t$, le problème final se présente comme suit:
$$ \large \hat{f}(x) = \sum_{i = 0}^{t-1} \hat{f_i}(x), \\
\large r_{it} = -\left[\frac{\partial L(y_i, f(x_i))}{\partial f(x_i)}\right]_{f(x)=\hat{f}(x)}, \quad \mbox{for } i=1,\ldots,n ,\\
\large \theta_t = \underset{\theta}{\arg\min} \ \sum_{i = 1}^{n} (r_{it} - h(x_i, \theta))^2, \\
\large \rho_t = \underset{\rho}{\arg\min} \ \sum_{i = 1}^{n} L(y_i, \hat{f}(x_i) + \rho \cdot h(x_i, \theta_t))$$

### L'algorithme GBM classique de Friedman

Nous pouvons maintenant définir l’algorithme GBM classique proposé par Jerome Friedman en 1999. C’est un algorithme supervisé qui a les composants suivants:

- ensemble de données $\large \left\{ (x_i, y_i) \right\}_{i=1, \ldots,n}$;
- nombre d'itérations $\large M$;
- choix de la fonction de perte $\large L(y, f)$ avec un gradient défini;
- choix de la famille de fonctions des algorithmes de base $\large h(x, \theta)$ avec la procédure d'apprentissage;
- hyperparamètres supplémentaires $\large h(x, \theta)$ (par exemple, dans les arbres de décision, la profondeur de l’arbre);

La seule chose qui reste est l'approximation initiale $\large f_0(x)$. Pour simplifier, pour une approximation initiale, une valeur constante $\large \gamma$ est utilisée. La valeur constante, ainsi que le coefficient optimal $\large \rho $, sont identifiés via une recherche binaire ou un autre algorithme de recherche de ligne sur la fonction de perte initiale (et non un gradient). Nous avons donc notre algorithme GBM décrit comme suit:

1. Initialiser GBM avec une valeur constante $\large \hat{f}(x) = \hat{f}_0, \hat{f}_0 = \gamma,  \gamma \in \mathbb{R}$
$\large \hat{f}_0 = \underset{\gamma}{\arg\min} \ \sum_{i = 1}^{n} L(y_i, \gamma)$
2. Pour chaque itération $\large t = 1, \dots, M$, répéter :
3. Calculer les pseudo-résidus $\large r_t$
$\large r_{it} = -\left[\frac{\partial L(y_i, f(x_i))}{\partial f(x_i)}\right]_{f(x)=\hat{f}(x)}, \quad \mbox{for } i=1,\ldots,n$
4. Construire le nouvel algorithme de base $\large h_t(x)$ sous forme de régression sur les pseudo-résidus $\large \left\{ (x_i, r_{it}) \right\}_{i=1, \ldots,n}$
5. Trouver le coefficient optimal $\large \rho_t $ à $\large h_t(x)$ concernant la fonction de perte initiale
$\large \rho_t = \underset{\rho}{\arg\min} \ \sum_{i = 1}^{n} L(y_i, \hat{f}(x_i) +  \rho \cdot h(x_i, \theta))$
6. Enregistrer $\large \hat{f_t}(x) = \rho_t \cdot h_t(x)$
7. Mettre à jour l'approximation courante $\large \hat{f}(x)$
$\large \hat{f}(x) \leftarrow \hat{f}(x) + \hat{f_t}(x) = \sum_{i = 0}^{t} \hat{f_i}(x)$
8. Composez le mrdèle final GBM $\large \hat{f}(x)$
$\large \hat{f}(x) = \sum_{i = 0}^M \hat{f_i}(x) $
9. Conquérir Kaggle et le reste du monde

### Exemple pas à pas: comment fonctionne la GBM

Voyons un exemple du fonctionnement de la GBM. Dans cet exemple de basé sur un jeu, nous allons restaurer une fonction bruyante $\large y = cos(x) + \epsilon, \epsilon \sim \mathcal{N}(0, \frac{1}{5}), x \in [-5,5]$.

Il s’agit d’un problème de régression avec une cible à valeur réelle. Nous allons donc choisir d’utiliser la fonction de perte d’erreur quadratique moyenne. Nous allons générer 300 paires d'observations et les approximer avec des arbres de décision de profondeur 2. Réunissons tout ce dont nous avons besoin pour utiliser GBM:
- Jeu de données $\large \left\{ (x_i, y_i) \right\}_{i=1, \ldots,300}$ ✓
- Nombre d'itérations $\large M = 3$ ✓;
- La fonction de perte d'erreur quadratique moyenne $\large L(y, f) = (y-f)^2$ ✓
- Le gradient de perte en $\large L(y, f) = L_2$ n'est que des résidus $\large r = (y - f)$ ✓;
- Arbre de décision en tant qu'algorithme de base $\large h(x)$ ✓;
- Hyperparamètres des arbres de décision : la profondeur des arbres est égale à 2 ✓;

Pour l'erreur quadratique moyenne, l'initialisation $\large \gamma$ et les coefficients $\large \rho_t$ sont simples. Nous initialiserons GBM avec la valeur moyenne $\large \gamma = \frac{1}{n} \cdot \sum_{i = 1}^n y_i$ et définirons tous les coefficients $\large \rho_t$ sur 1.

Nous allons lancer GBM et dessiner deux types de graphes : l’approximation courante $\large \hat{f}(x)$ (graphe bleu) et chaque arbre $\large \hat{f_t}(x)$ construit sur ses pseudo-résidus (graphe vert). Le numéro du graphique correspond au numéro d'itération :

À la deuxième itération, nos arbres ont retrouvé la forme de base de la fonction. Cependant, à la première itération, nous voyons que l'algorithme n'a construit que la "branche gauche" de la fonction ($\large x \in [-5, -4]$). Cela était dû au fait que nos arbres n’avaient tout simplement pas assez de profondeur pour construire une branche symétrique à la fois et qu’ils se concentraient sur la branche gauche présentant l’erreur la plus grande. Par conséquent, la branche de droite n'est apparue qu'après la deuxième itération.

Le reste du processus se déroule comme prévu : à chaque étape, nos pseudo-résidus ont diminué et GBM a amélioré de mieux en mieux la fonction d'origine à chaque itération. Cependant, par construction, les arbres ne peuvent pas se rapprocher d'une fonction continue, ce qui signifie que GBM n'est pas idéal dans cet exemple. Pour jouer avec les approximations de fonctions GBM, vous pouvez utiliser la démo interactive impressionnante de ce blog intitulée [Brilliantly wrong](http://arogozhnikov.github.io/2016/06/24/gradient_boosting_explained.html) :

# 3. Fonctions de perte

Si nous voulons résoudre un problème de classification au lieu d'une régression, qu'est-ce qui changerait ? Il suffit de choisir une fonction de perte appropriée, $\large L(y, f)$. C’est le moment le plus important qui détermine exactement comment nous allons optimiser et à quelles caractéristiques nous pouvons nous attendre dans le modèle final.

En règle générale, nous n'avons pas besoin de l'inventer nous-mêmes : les chercheurs l'ont déjà fait pour nous. Aujourd'hui, nous allons explorer les fonctions de perte pour les deux objectifs les plus courants : la régression $\large y \in \mathbb{R}$ et la classification binaire $\large y \in \left\{-1, 1\right\}$.

### Fonctions de perte liées à la  régression

Commençons par un problème de régression pour $\large y \in \mathbb{R}$. Afin de choisir la fonction de perte appropriée, nous devons déterminer quelles propriétés de la distribution conditionnelle $\large (y|x)$ nous souhaitons restaurer. Les options les plus courantes sont:

- $\large L(y, f) = (y - f)^2$ a.k.a. $\large L_2$ perte ou perte gaussienne. C'est la moyenne conditionnelle classique, qui est le cas le plus simple et le plus courant. Si nous n'avons pas d'informations supplémentaires ou d'exigences pour qu'un modèle soit robuste, nous pouvons utiliser la perte gaussienne.
- perte $\large L_1$ ou $\large L_1$ ou perte laplacienne. Au premier abord, cette fonction ne semble pas pouvoir être différenciée, mais définit en réalité la médiane conditionnelle. Comme nous le savons, la médiane est robuste aux valeurs aberrantes, raison pour laquelle cette fonction de perte est meilleure dans certains cas. La pénalité pour les grandes variations n’est pas aussi lourde que dans $\large L_2$.
- $ \large \begin{equation}  L(y, f) =\left\{   \begin{array}{@{}ll@{}}     (1 - \alpha) \cdot |y - f|, & \text{if}\ y-f \leq 0 \\     \alpha \cdot |y - f|, & \text{if}\ y-f >0  \end{array}\right. \end{equation}, \alpha \in (0,1)
$ a.k.a. $\large L_q$ perte ou perte de Quantile. Au lieu de la médiane, il utilise des quantiles. Par exemple, $\large \alpha = 0.75$ correspond au 75%-quantile. Nous pouvons voir que cette fonction est asymétrique et pénalise les observations qui se trouvent du côté droit du quantile défini.

Utilisons la fonction de perte $\large L_q$ sur nos données. L'objectif est de restaurer le quantile conditionnel de cosinus à 75%. Mettons tout en œuvre pour GBM:
- Jeu de données $\large \left\{ (x_i, y_i) \right\}_{i=1, \ldots,300}$ ✓
- Un nombre d'itérations $\large M = 3$ ✓;
- Fonction de perte pour les quantiles $ \large \begin{equation}   L_{0.75}(y, f) =\left\{
\begin{array}{@{}ll@{}}    0.25 \cdot |y - f|, & \text{if}\ y-f \leq 0 \\     0.75 \cdot |y - f|, & \text{if}\ y-f >0   \end{array}\right. \end{equation} $ ✓;
- Gradient $\large L_{0.75}(y, f)$ - fonction pondérée par $\large \alpha = 0.75$. Nous allons former un modèle basé sur des arbres pour la classification :
$\large r_{i} = -\left[\frac{\partial L(y_i, f(x_i))}{\partial f(x_i)}\right]_{f(x)=\hat{f}(x)} = $
$\large = \alpha I(y_i > \hat{f}(x_i) ) - (1 - \alpha)I(y_i \leq \hat{f}(x_i) ), \quad \mbox{for } i=1,\ldots,300$ ✓;
- Arbre de décision en tant qu'algorithme de base $\large h(x)$ ✓;
- Hyperparamètre des arbres : profondeur = 2 ✓;

Pour notre approximation initiale, nous prendrons le quantile nécessaire de $\large y$. Cependant, nous ne savons rien des coefficients optimaux $\large \rho_t$, nous allons donc utiliser la recherche par ligne standard. Les résultats sont les suivants :

Nous pouvons observer que, à chaque itération, $\large r_{i} $ ne prend que 2 valeurs possibles, mais GBM est toujours capable de restaurer notre fonction initiale.

Les résultats globaux de GBM avec fonction de perte quantile sont les mêmes que les résultats avec fonction de perte quadratique décalée par $\large \approx 0.135$. Mais si nous utilisions le 90%-quantile, nous n'aurions pas assez de données car les classes seraient déséquilibrées. Nous devons nous en souvenir lorsque nous traitons des problèmes non standard.

*"Quelques mots sur les fonctions de perte de régression"*

Pour les tâches de régression, de nombreuses fonctions de perte ont été développées, certaines avec des propriétés supplémentaires. Par exemple, ils peuvent être robustes, comme dans la [fonction de perte de Huber](https://en.wikipedia.org/wiki/Huber_loss). Pour un petit nombre de valeurs aberrantes, la fonction de perte fonctionne comme $\large L_2$, mais après un seuil défini, la fonction devient $\large L_1$. Cela permet de réduire l'effet des valeurs aberrantes et de se concentrer sur l'image globale.

Nous pouvons illustrer cela avec l'exemple suivant. Les données sont générées à partir de la fonction $\large y = \frac{sin(x)}{x}$ avec ajout de bruit, un mélange de distributions normales et de distributions de Bernulli. Nous montrons les fonctions sur les graphes A-D et le GBM correspondant sur F-H (le graphe E représente la fonction initiale):

 [Taille originale](https://habrastorage.org/web/130/05b/222/13005b222e8a4eb68c3936216c05e276.jpg).

Dans cet exemple, nous avons utilisé des splines comme algorithme de base. Vous voyez, il ne faut pas toujours que ce soit des arbres pour le Bosting ?

Nous pouvons clairement voir la différence entre les fonctions $\large L_2$, $\large L_1$ et la perte de Huber. Si nous choisissons des paramètres optimaux pour la perte de Huber, nous pouvons obtenir la meilleure approximation possible parmi toutes nos options. La différence est également visible dans les quantiles de 10%, 50% et 90%.

Malheureusement, la fonction de perte de Huber n’est prise en charge que par très peu de bibliothèques / packages populaires; h2o le supporte, mais pas XGBoost. Il est pertinent pour d'autres choses plus exotiques comme les [expectiles conditionnelles](https://www.slideshare.net/charthur/quantile-and-expectile-regression), mais il peut quand même s'agir de connaissances intéressantes.

### Fonctions de perte de classification

Maintenant, regardons le problème de classification binaire $\large y \in \left\{-1, 1\right\}$. Nous avons vu que GBM peut même optimiser des fonctions de perte non différenciables. Techniquement, il est possible de résoudre ce problème avec une perte de régression $\large L_2$, mais ce ne serait pas correct.

La distribution de la variable cible nécessite que nous utilisions un "log-likehood", il nous faut donc différentes fonctions de perte pour les cibles multipliées par leurs prédictions : $\large y \cdot f$. Les choix les plus courants seraient les suivants:

- $\large L(y, f) = log(1 + exp(-2yf))$ ak.a. Perte logistique ou perte de Bernoulli. Cela a une propriété intéressante qui pénalise même les classes correctement prédites, ce qui aide non seulement à optimiser la perte, mais également à écarter davantage les classes, même si toutes les classes sont correctement prédites.
- perte de $\large L(y, f) = exp(-yf)$ a.k.a. AdaBoost. Le classique AdaBoost est équivalent à GBM avec cette fonction de perte. Conceptuellement, cette fonction est très similaire à la perte logistique, mais elle est plus pénalisée de façon exponentielle si la prédiction est fausse.

Générons un nouveau jeu de données pour notre problème de classification. En guise de base, nous prendrons notre cosinus "bruyant" et nous utiliserons la fonction de signe pour les classes de la variable cible. Nos données ressemblent à ceci (le "jitter-noise" est ajouté pour plus de clarté) :

Nous utiliserons la perte logistique pour rechercher ce que nous améliorons réellement. Donc, encore une fois, nous avons mis en place ce que nous allons utiliser pour GBM :
- jeu de données $\large \left\{ (x_i, y_i) \right\}_{i=1, \ldots,300}, y_i \in \left\{-1, 1\right\}$ ✓
- Nombre d'itérations $\large M = 3$ ✓;
- Perte logistique en tant que fonction de perte, son gradient est calculé de la manière suivante :
$\large r_{i} = \frac{2 \cdot y_i}{1 + exp(2 \cdot y_i \cdot \hat{f}(x_i)) }, \quad \mbox{for } i=1,\ldots,300$ ✓;
- Arbre de décision en tant qu'algorithme de base $\large h(x)$ ✓;
- Hyperparamètres des arbres de décision : la profondeur de l'arbre est égale à 2 ✓;

Cette fois, l'initialisation de l'algorithme est un peu plus difficile. Premièrement, nos classes sont déséquilibrées (63% contre 37%). Deuxièmement, il n’existe pas de formule analytique connue pour l’initialisation de notre fonction de perte, nous devons donc rechercher $\large \hat{f_0} = \gamma$ via search :

Notre approximation initiale optimale est d'environ -0,273. Vous auriez pu deviner que c'était négatif car il est plus rentable de tout prédire comme la classe la plus populaire, mais il n'y a pas de formule pour la valeur exacte. Maintenant, commençons enfin par GBM, et regardons ce qui se passe réellement sous le capot:

L'algorithme a restauré avec succès la séparation entre nos classes. Vous pouvez voir comment les zones "inférieures" se séparent car les arbres sont plus confiants dans la prédiction correcte de la classe négative et comment se forment les deux étapes des classes mixtes. Il est clair que nous avons beaucoup d'observations correctement classées et un certain nombre d'observations avec des erreurs importantes qui sont apparues en raison du bruit dans les données.

### Poids

Parfois, nous voulons une fonction de perte plus spécifique pour notre problème. Par exemple, dans les séries chronologiques financières, il se peut que nous souhaitions accorder plus de poids aux mouvements importants dans la série chronologique; pour la prévision du taux de désabonnement, il est plus utile de prédire le désabonnement des clients ayant un LTV élevé (ou une valeur à vie: combien d'argent un client rapportera-t-il à l'avenir ?).

Le guerrier statistique inventerait sa propre fonction de perte, en écrivant le gradient (pour un entraînement plus efficace, incluez le Hessian) et vérifierait avec soin si cette fonction remplissait les propriétés requises. Cependant, il y a de fortes chances que quelqu'un commette une erreur quelque part, se heurte à des difficultés de calcul et consacre une quantité excessive de temps à la recherche.

Au lieu de cela, un instrument très simple a été inventé (ce dont on se souvient rarement dans la pratique) : peser des observations et attribuer des fonctions de pondération. L'exemple le plus simple d'une telle pondération est la définition de pondérations pour la balance de classes. En général, si nous savons qu'un sous-ensemble de données, tant dans les variables d'entrée $\large x$ que dans la variable cible $\large y$, a une plus grande importance pour notre modèle, nous leur attribuons simplement une pondération plus importante, $\large w(x,y)$. L’objectif principal est de satisfaire aux exigences générales en matière de poids:
$$ \large w_i \in \mathbb{R}, \\
\large w_i \geq 0 \quad \mbox{for } i=1,\ldots,n, \\
\large \sum_{i = 1}^n w_i > 0 $$

Les poids peuvent réduire considérablement le temps passé à ajuster la fonction de perte à la tâche que nous résolvons et encourager les expériences sur les propriétés des modèles cibles. L'attribution de ces poids est entièrement fonction de la créativité. Nous ajoutons simplement des poids scalaires:
$$ \large L_{w}(y,f) = w \cdot L(y,f), \\
\large r_{it} =   - w_i \cdot \left[\frac{\partial L(y_i, f(x_i))}{\partial f(x_i)}\right]_{f(x)=\hat{f}(x)}, \quad \mbox{for } i=1,\ldots,n$$

Il est clair que, pour les poids arbitraires, nous ne connaissons pas les propriétés statistiques de notre modèle. Lier les poids aux valeurs $\large y$ peut souvent être trop compliqué. Par exemple, l'utilisation de poids proportionnels à $\large |y|$ dans la fonction de perte $\large L_1$ n'est pas équivalente à la perte de $\large L_2$ car le gradient ne prendra pas en compte les valeurs des prédictions elles-mêmes : $\large \hat{f}(x)$.

Nous mentionnons tout cela afin de mieux comprendre nos possibilités. Créons des poids très exotiques pour notre jeu de données. Nous allons définir une fonction de poids fortement asymétrique comme suit :
$$ \large \begin{equation} w(x) =\left\{   \begin{array}{@{}ll@{}}     0.1, & \text{if}\ x \leq 0 \\     0.1 + |cos(x)|, & \text{if}\ x >0 \end{array}\right. \end{equation} $$

Avec ces poids, nous nous attendons à obtenir deux propriétés : moins de détails pour les valeurs négatives de $\large x$ et la forme de la fonction, similaire au cosinus initial. Nous reprenons les réglages des autres GBM de notre exemple précédent avec une classification incluant la recherche par ligne pour les coefficients optimaux. Regardons ce que nous avons :

Nous avons atteint le résultat escompté. Premièrement, nous pouvons voir à quel point les pseudo-résidus diffèrent fortement; à l'itération initiale, ils ressemblent presque au cosinus d'origine. Deuxièmement, la partie gauche du graphique de la fonction était souvent ignorée au profit de la droite, qui avait des poids plus importants. Troisièmement, la fonction que nous avons obtenue à la troisième itération a reçu suffisamment d’attention et a commencé à ressembler au cosinus original (elle a également légèrement sur-appris)

Les poids sont un outil puissant mais risqué que nous pouvons utiliser pour contrôler les propriétés de notre modèle. Si vous souhaitez optimiser votre fonction de perte, essayez d'abord de résoudre un problème plus simple en ajoutant des pondérations aux observations, à votre discrétion.

# 4. Conclusion

Aujourd'hui, nous avons appris la théorie derrière le Gradient Boosting. GBM n'est pas seulement un algorithme spécifique, mais une méthodologie commune pour la construction d'ensembles de modèles. De plus, cette méthodologie est suffisamment flexible et extensible - il est possible de former un grand nombre de modèles en tenant compte de différentes fonctions de perte avec une variété de fonctions de pondération.

La pratique et les compétitions ML montrent que, dans les problèmes classiques (à l’exception des images, de l’audio et des données très éparses), la GBM est souvent l’algorithme le plus efficace (pour ne pas mentionner les ensembles superposés et de haut niveau, où la GBM fait presque toujours partie intégrante). En outre, il existe de nombreuses adaptations de GBM [pour l'apprentissage par renforcement](https://arxiv.org/abs/1603.04119) (Minecraft, ICML 2016). En passant, l'algorithme Viola-Jones, qui est encore utilisé en vision par ordinateur, est basé sur [AdaBoost](https://en.wikipedia.org/wiki/Viola%E2%80%93Jones_object_detection_framework#Learning_algorithm).

Dans cet article, nous avons volontairement omis de poser des questions sur la régularisation, la stochasticité et les hyper-paramètres de GBM. Ce n'est pas par hasard que nous avons utilisé un petit nombre d'itérations $\large M = 3$. Si nous utilisions 30 arbres au lieu de 3 et formions le GBM comme décrit, le résultat ne serait pas prévisible :

[Démo interactive](http://arogozhnikov.github.io/2016/07/05/gradient_boosting_playground.html)

# 5. Mission de démonstration
Votre tâche consiste à battre les bases de référence dans le cadre de la compétition "Flight delays" de [Kaggle Inclass](https://www.kaggle.com/c/flight-delays-fall-2018). Vous recevez un [démarreur CatBoost](https://www.kaggle.com/kashnitsky/mlcourse-ai-fall-2019-catboost-starter), l'astuce consiste à proposer de bonnes caractéristiques (features).

# 6. Ressources utiles
- Liste des cours [site](https://mlcourse.ai), [course repo](https://github.com/Yorko/mlcourse.ai), and YouTube [channel](https://www.youtube.com/watch?v=QKTuw4PNOsU&list=PLVlY_7IJCMJeRfZ68eVfEcu-UcN9BbwiX)
- Course materials as a [Kaggle Dataset](https://www.kaggle.com/kashnitsky/mlcourse)
- mlcourse.ai lectures on gradient boosting: [theory](https://youtu.be/g0ZOtzZqdqk) and [practice](https://youtu.be/V5158Oug4W8)
- [Original article](https://statweb.stanford.edu/~jhf/ftp/trebst.pdf) about GBM from Jerome Friedman
- “Gradient boosting machines, a tutorial”, [paper](https://www.ncbi.nlm.nih.gov/pmc/articles/PMC3885826/) by Alexey Natekin, and Alois Knoll
- [Chapter in Elements of Statistical Learning](http://statweb.stanford.edu/~tibs/ElemStatLearn/printings/ESLII_print10.pdf) from Hastie, Tibshirani, Friedman (page 337)
- [Wiki](https://en.wikipedia.org/wiki/Gradient_boosting) article about Gradient Boosting
- [Introduction to boosted trees (Xgboost docs)](https://xgboost.readthedocs.io/en/latest/tutorials/model.html)
- [Video-lecture by Hastie](https://www.youtube.com/watch?v=wPqtzj5VZus) about GBM at h2o.ai conference
- [CatBoost vs. Light GBM vs. XGBoost](https://towardsdatascience.com/catboost-vs-light-gbm-vs-xgboost-5f93620723db) on "Towards Data Science"
- [Benchmarking and Optimization of
Gradient Boosting Decision Tree Algorithms](https://arxiv.org/abs/1809.04559), [XGBoost: Scalable GPU Accelerated Learning](https://arxiv.org/abs/1806.11248) - benchmarking CatBoost, Light GBM, and XGBoost (no 100% winner)