gradient
Gradient is an important concept used in calculus that measures the maximum value of a function at a given point when its directional derivative is maximum in all directions. For a scalar function, the direction of the gradient is the direction in which the function grows fastest, and the opposite direction of the gradient is the direction in which the function decreases fastest.
definition
For a function $f: \mathbb{R}^n \rightarrow \mathbb{R}$ that is differentiable at point $x \in \mathbb{R}^n$, its gradient is defined as a vector, and its components are the partial derivatives of the function at that point. For function $f(x_1, x_2, …, x_n)$, its gradient can be expressed as:
$$
\nabla f(x) = \left[ \frac{\partial f}{\partial x_1}, \frac{\partial f}{\partial x_2}, …, \frac{\partial f}{\partial x_n} \right]^T
$$
Here, $\nabla f(x)$ represents the gradient of $f(x)$, $\frac{\partial f}{\partial x_i}$ represents the partial derivative of $f$ with respect to $x_i$, and $T$ represents the matrix transpose.
physical meaning
Gradient has an important physical meaning. In two-dimensional space, the function $f(x, y)$ can be regarded as the height of the terrain, then the gradient is the vector pointing in the steepest rising direction. The magnitude of the gradient corresponds to the slope of the steepest upward direction. Therefore, in optimization problems, we usually update parameters in the opposite direction of the gradient to reduce the function value the fastest.
Application of gradients in machine learning
In machine learning, our goal is usually to find a set of parameters that minimizes the loss function. To achieve this goal, we can use the gradient descent algorithm to continuously update the parameters in the opposite direction of the gradient of the loss function.
In deep learning, since models usually have a large number of parameters, we need to use the backpropagation algorithm to calculate gradients efficiently. This algorithm is based on the chain rule and can calculate the gradient of each parameter layer by layer from the output end to the input end in the calculation graph.
Calculate gradient
In practice, we usually use automatic differentiation (such as the automatic differentiation function provided by PyTorch and TensorFlow) to calculate the gradient. This eliminates the need for us to manually derive and implement complex gradient formulas, greatly improving programming efficiency. The following is an example of calculating gradients in PyTorch:
1 | import torch |
In the above example,y.backward()represents the calculation aboutythe gradient of , and then backpropagates this gradient back to its inputx. Therefore,yThe gradient itself is considered to be 1 (since for any variablex,dx/dxare both equal to 1), then this gradient is passed tox, what we get isyAboutxThe gradient ofdy/dx。
Note thatyNone per se.gradattribute because it is not passedrequires_grad=TrueCreated. only those who passrequires_grad=TrueCreated and participated in the operation of the tensor.gradAttribute, this attribute stores gradient information.
In PyTorch,backward()The function is to calculate the gradient and store the gradient information in.gradin properties.backward()The caller of the function (i.e.y)'s own gradient is considered to be 1, and then this gradient is backpropagated back to all tensors that participate in the operation and need to calculate the gradient.
gradient descent algorithm
Principle
The gradient descent algorithm is an iterative method for optimizing an objective function. Specifically, it is an algorithm for finding the minimum of a function. For a maximization problem, we can solve it by minimizing the inverse of the objective function.
In gradient descent, we first choose an initial point (i.e., initial parameter value), and then we iteratively move the parameters toward the negative gradient direction, so that at each step, we are able to reduce the value of the objective function until we find a local minimum of the function.
In this process, the gradient (the first derivative of the function) gives the direction in which the value of the function decreases fastest. We use a parameter called the learning rate to control the size of each step. The learning rate determines the step size of parameter update in each iteration. A learning rate that is too large may cause the algorithm to oscillate at the minimum value, and a learning rate that is too small may cause the algorithm to converge too slowly.
formula
The basic gradient descent update formula is:
$$
\theta_{new} = \theta_{old} - \alpha \nabla J(\theta_{old})
$$
where $\theta$ represents the parameter we are trying to optimize, $J(\theta)$ is the objective function we are trying to minimize, $\nabla J(\theta_{old})$ is the gradient of the objective function at the current parameter value $\theta_{old}$, and $\alpha$ is the learning rate.
Example
Consider a simple linear regression problem. We have an objective function (loss function) which is the mean squared error:
$$
J(\theta) = \frac{1}{2m} \sum_{i=1}^{m} (h_{\theta}(x^{(i)}) - y^{(i)})^2
$$
Among them, $h_{\theta}(x^{(i)}) = \theta^T x^{(i)}$ is the prediction function, $m$ is the number of training samples.
For this problem, we can use the gradient descent algorithm to find the parameters $\theta$ that minimize the loss function. In each iteration, we first calculate the gradient of the loss function and then update the parameters according to the previous formula.
Code implementation
Here is sample code for implementing gradient descent in Python:
1 | import numpy as np |
In this code,gradient_descent The function implements the gradient descent algorithm,compute_cost Function is used to calculate the value of the objective function (loss function).
Classification of Gradient Descent Algorithms
Gradient descent algorithm is an optimization algorithm used to find the minimum value of the loss function. In machine learning and deep learning, we usually use the gradient descent algorithm to optimize our models, that is, adjust the model parameters to minimize the loss function. Several major variations of gradient descent will be introduced below.
Batch Gradient Descent
Batch gradient descent is the most basic form and uses the entire training data to calculate the gradient of the loss function in each iteration. Therefore, each step of batch gradient descent is towards the global optimum. However, this also makes batch gradient descent very slow on large-scale data sets and unable to update the model online (real-time).
Update formula:
$$
\theta = \theta - \alpha \nabla J(\theta)
$$
Among them, $\theta$ is the parameter, $\alpha$ is the learning rate, $\nabla J(\theta)$ is the gradient of the loss function $J$ with respect to the parameter $\theta$.
Stochastic Gradient Descent (SGD)
Stochastic gradient descent randomly selects a sample to calculate the gradient in each iteration. Therefore, the update direction at each step is not necessarily the globally optimal direction, and the result will have some noise. However, this makes stochastic gradient descent much faster than batch gradient descent on large-scale data sets, and enables online model updates.
The update formula is the same as batch gradient descent, except that it is only calculated on one random sample at a time.
Mini-Batch Gradient Descent
Mini-batch gradient descent is a compromise between batch gradient descent and stochastic gradient descent, which uses a portion (mini-batch) of samples in each iteration to calculate the gradient. Mini-batch gradient descent is more stable than stochastic gradient descent while still having a relatively high computational speed.
The update formula is consistent with the previous two, except that it is calculated for a small batch of samples each time.
Advanced variants of the gradient descent algorithm
On the basis of gradient descent, researchers have introduced some additional concepts to improve the performance of the algorithm. Below are some widely used variations of the gradient descent algorithm.
Momentum
Momentum is a strategy that helps the optimizer maintain speed in the relevant direction, thus suppressing oscillations and speeding up convergence. The core idea is to introduce a new variable (often called speed) that increases the current gradient at each step, and then parameter updates proceed at this speed.
The update formula for Momentum is as follows:
$$
v = \beta v - \alpha \nabla J(\theta)
$$
$$
\theta = \theta + v
$$
Among them, $\theta$ is the parameter, $\nabla J(\theta)$ is the gradient of the loss function $J$ with respect to the parameter $\theta$, $\alpha$ is the learning rate, $v$ is the speed, $\beta$ is the momentum factor, usually set to 0.9.
AdaGrad
The main idea of AdaGrad (Adaptive Gradient Algorithm) is to assign an adaptive learning rate to each parameter, which is very useful for sparse data and processing non-stationary objective functions. Specifically, for parameters that appear frequently and have large gradients, the learning rate will be reduced; conversely, for parameters that are sparse or have small gradients, the learning rate will be increased.
The update formula of AdaGrad is as follows:
$$
G_{t} = G_{t-1} + (\nabla J(\theta))^2
$$
$$
\theta = \theta - \frac{\alpha}{\sqrt{G_{t} + \epsilon}} \cdot \nabla J(\theta)
$$
Among them, $G_{t}$ is the sum of squares of all gradients so far, and $\epsilon$ is a small number, usually set to 1e-8, to prevent division by zero errors.
RMSProp
RMSProp (Root Mean Square Propagation) is an improved version of AdaGrad, which mainly solves the problem of AdaGrad's rapid decline in learning rate under non-convex settings. Like AdaGrad, RMSProp also assigns an adaptive learning rate to each parameter, but it uses a sliding average of the squared gradient to update $G_{t}$.
The update formula of RMSProp is as follows:
$$
G_{t} = \beta G_{t-1} + (1 - \beta) (\nabla J(\theta))^2
$$
$$
\theta = \theta - \frac{\alpha}{\sqrt{G_{t} + \epsilon}} \cdot \nabla J(\theta)
$$
Among them, $\beta$ is the moving average factor of the squared gradient, usually set to 0.9.
Adam
Adam (Adaptive Moment Estimation) combines the ideas of Momentum and RMSProp. It computes an exponential moving average of the gradient (first moment) and an exponential moving average of the squared gradient (second moment) and uses these two quantities to update the parameters.
Adam's update formula is as follows:
$$
m = \beta_{1} m + (1 - \beta_{1}) \nabla J(\theta)
$$
$$
v = \beta_{2} v + (1 - \beta_{2}) (\nabla J(\theta))^2
$$
$$
\hat{m} = \frac{m}{1 - \beta_{1}^{t}}
$$
$$
\hat{v} = \frac{v}{1 - \beta_{2}^{t}}
$$
$$
\theta = \theta - \frac{\alpha \hat{m}}{\sqrt{\hat{v}} + \epsilon}
$$
Among them, $m$ and $v$ are the estimates of the first moment and the second moment respectively, $\beta_{1}$ and $\beta_{2}$ are the moving average factors of the first moment and the second moment respectively, usually set to 0.9 and 0.999, and $t$ is the current iteration step number.
Detailed introduction to PyTorch optimizer
PyTorch provides some already implemented optimizers, which are in the torch.optim module. The main role of the optimizer is to update the parameters of the model to minimize the objective function (usually the loss function).
Introduction and use of common optimizers
- Stochastic Gradient Descent (SGD)
SGD is the most basic optimizer, which updates each parameter using the same learning rate. Here is its updated formula:
$$
\theta_{new} = \theta_{old} - \alpha \nabla J(\theta_{old})
$$
In PyTorch, you can use the SGD optimizer like this:
1 | optimizer = torch.optim.SGD(model.parameters(), lr=0.1) |
- Stochastic Gradient Descent with Momentum SGD
Momentum SGD is an improvement of SGD. It considers past gradients when updating parameters to achieve a smooth update effect. Here is its updated formula:
$$
v = \beta v - \alpha \nabla J(\theta)
$$
$$
\theta = \theta + v
$$
Among them, $v$ is the momentum and $\beta$ is the momentum attenuation factor. In PyTorch, you can use the Momentum SGD optimizer like this:
1 | optimizer = torch.optim.SGD(model.parameters(), lr=0.1, momentum=0.9) |
- Adaptive Gradient Algorithm (Adagrad)
Adagrad is an adaptive learning rate optimizer that updates each parameter using a different learning rate. This makes it perform well when dealing with sparse data. Here is its updated formula:
$$
\theta_{new} = \theta_{old} - \frac{\alpha}{\sqrt{G_{t} + \epsilon}} \cdot \nabla J(\theta_{old})
$$
Among them, $G_{t}$ is the sum of squares of all gradients so far, and $\epsilon$ is a small number (such as 1e-8) to prevent division by zero errors. In PyTorch, you can use the Adagrad optimizer like this:
1 | optimizer = torch.optim.Adagrad(model.parameters(), lr=0.1) |
- Adaptive momentum estimation (Adam)
Adam combines the ideas of Momentum SGD and Adagrad. It has an adaptive learning rate for each parameter and takes into account past gradients. This makes it perform well on many tasks. Here is its updated formula:
$$
m = \beta_{1} m + (1 - \beta_{1}) \nabla J(\theta)
$$
$$
v = \beta_{2} v + (1 - \beta_{2}) (\nabla J(\theta))^2
$$
$$
\hat{m} = \frac{m}{1 - \beta_{1}^{t}}
$$
$$
\hat{v} = \frac{v}{1 - \beta_{2}^{t}}
$$
$$
\theta = \theta - \frac{\alpha \hat{m}}{\sqrt{\hat{v}} + \epsilon}
$$
Among them, $m$ and $v$ are the first and second moment estimates respectively, $\beta_{1}$ and $\beta_{2}$ are attenuation factors (generally set to 0.9 and 0.999), and $t$ is the number of iterations. In PyTorch, you can use the Adam optimizer like this:
1 | optimizer = torch.optim.Adam(model.parameters(), lr=0.001) |
How to use the optimizer
The basic usage process of the optimizer is as follows:
- Define the model.
- Define the loss function.
- Select the optimizer and pass the model parameters and learning rate into the optimizer.
- In the training loop, the gradient of the optimizer is first cleared (optimizer.zero_grad()), then the loss is calculated (loss.backward()), and finally the parameters of the model are updated (optimizer.step()).
Optimizer selection scenarios
- SGD: Suitable for large-scale linear models, or to quickly reduce loss in the early stages of training.
- Momentum SGD: Suitable for deep learning tasks and has faster convergence speed than SGD.
- Adagrad: Suitable for tasks dealing with sparse data, such as word vector training in natural language processing.
- Adam: Suitable for most deep learning tasks and is a more general optimizer.
There are no fixed rules for choosing which optimizer to choose. It depends on the characteristics of the task and the characteristics of the data. Generally speaking, you can try Adam first, and if the effect is not good, consider other optimizers.
Back propagation algorithm
Backpropagation is one of the main algorithms used to train models in neural networks. It is a core part of many modern deep learning frameworks such as TensorFlow and PyTorch. The following describes how backpropagation works in detail. Its main task is to efficiently update the weights and biases of the network by computing the gradient of the loss function (a function that measures the difference between the model prediction and the true value) on the model parameters to minimize the loss function.
Principle
The goal of backpropagation is to compute the gradient of the loss function with respect to the neural network parameters in order to update the parameters using gradient descent or other optimization algorithms. To do this, backpropagation starts at the output layer and propagates gradients backwards along the neural network.
The key idea of backpropagation is the chain rule, which is a fundamental theorem of calculus and is used to calculate the derivatives of composite functions. In the context of neural networks, the chain rule is used to calculate the gradient of a loss function with respect to parameters, by splitting this composite function into a series of simpler functions and multiplying their derivatives.
algorithm process
The following are the general steps of the backpropagation algorithm:
forward propagation: Starting from the input layer, the data is propagated forward through the network, and the output of each layer is calculated until the final prediction result is obtained.
Calculate losses: Use the loss function to calculate the error between the predicted result and the actual target.
Back propagation loss: Starting from the output layer, calculate the gradient of the loss function with respect to the output of each layer, and backpropagate these gradients. Specifically, for each layer, we first calculate the gradient of the loss function with respect to the output of this layer, and then use the chain rule to calculate the gradient of this gradient with respect to the input of this layer, and the gradient with respect to the parameters of this layer.
Update parameters: Use gradient descent or other optimization algorithms to update network parameters using the gradients calculated in step 3.
This process is repeated in each training iteration until the network parameters converge, or a preset maximum number of iterations is reached.
Calculation method
Suppose we have a loss function $L$ and we want to know its gradient with respect to the weight $w_{ij}$ (representing the weight from the $i$th neuron to the $j$th neuron), we can use the following formula:
$$
\frac{\partial L}{\partial w_{ij}} = \frac{\partial L}{\partial z_j} \cdot \frac{\partial z_j}{\partial w_{ij}}
$$
Among them, $z_j$ is the input of $j$ neurons, which is equal to $\sum_i w_{ij} x_i + b_j$.
Now we need to find the derivative of each part. First, $\frac{\partial L}{\partial z_j}$ is usually directly provided by the later part of the network. This is because we generally calculate the derivatives in order from back to front, that is, we first calculate the derivative of the loss function with respect to the output of the last layer, and then use this derivative to calculate the derivative of the loss function with respect to the output of the penultimate layer, and so on.
Then, we need to find $\frac{\partial z_j}{\partial w_{ij}}$. Since $z_j = \sum_i w_{ij} x_i + b_j$, we can see that if we change $w_{ij}$, $z_j$ will change according to the size of $x_i$. Therefore, $\frac{\partial z_j}{\partial w_{ij}} = x_i$.
In this way, we get:
$$
\frac{\partial L}{\partial w_{ij}} = \frac{\partial L}{\partial z_j} \cdot x_i
$$
Let's look at a concrete example. Suppose we have a simple neural network with only input and output layers and no hidden layers. The input layer has one neuron with value $x$ and the output layer also has one neuron with value $y$. The weight between them is $w$ and the bias is $b$. Then, the calculation formula of $y$ is $y = wx + b$. We use the mean square error as the loss function, that is, if the true value is $t$, then the loss function is $L = (t - y)^2$.
Now, we want to calculate $\frac{\partial L}{\partial w}$. First, we need to find $\frac{\partial L}{\partial y}$. Since $L = (t - y)^2$, we can get $\frac{\partial L}{\partial y} = -2(t - y)$.
Then, we need to find $\frac{\partial y}{\partial w}$. Since $y = wx + b$, we get $\frac{\partial y}{\partial w} = x$.
So, in the end we get:
$$
\frac{\partial L}{\partial w} = \frac{\partial L}{\partial y} \cdot \frac{\partial y}{\partial w} = -2(t - y) \cdot x
$$
This is the gradient we are looking for, and we can use it to update the weights $w$ to reduce the loss function.
Code implementation
Backpropagation in PyyTorch
The following is a simple backpropagation algorithm PyTorch code example with a detailed step-by-step explanation:
- Create a simple neural network:
1 | model = nn.Sequential( |
First, we use PyTorchnn.SequentialLet’s define a simple neural network. This network consists of two linear layers (nn.Linear) and a ReLU activation function (nn.ReLU) composition. The first linear layer changes the size of the input from 10 to 20, the ReLU activation function increases the nonlinearity of the model, and the second linear layer maps the hidden state of size 20 to an output of size 1.
- Choosing a loss function and optimizer:
1 | criterion = nn.MSELoss() |
Next, we choose a loss function and an optimizer. The loss function is used to measure the gap between the model's predictions and the actual target, and the optimizer is used to update the parameters of the model based on the gradient of the loss. In this example, we choose the mean square error loss (nn.MSELoss) as the loss function, choose stochastic gradient descent (torch.optim.SGD) as an optimizer.
- Simulation input and target data:
1 | inputs = torch.randn(5, 10) |
We then generate some input and target data for the simulation. In this example, we generate an input tensor of shape [5, 10] and a target tensor of shape [5, 1].
- forward propagation:
1 | outputs = model(inputs) |
In the forward propagation stage, we pass the input data into the model and get the predicted output of the model.
- Calculate losses:
1 | loss = criterion(outputs, targets) |
Next, we use a loss function to calculate the gap between the model's predicted output and the actual target.
- Backpropagation:
1 | loss.backward() |
In the backpropagation stage, we call the loss tensorbackwardMethod that computes the gradient of the loss with respect to the model parameters. These gradients will be stored in the corresponding parameter.gradin properties.
- Update parameters:
1 | optimizer.step() |
Then, we call the optimizer'sstepmethod, based on the parameters stored in the model.gradGradient in properties to update parameters.
- Clear gradient:
1 | optimizer.zero_grad() |
Finally, we call the optimizer'szero_gradmethod, convert the model parameters into.gradThe gradient in the properties is cleared. This step is necessary because PyTorch accumulates gradients by default, i.e. each call.backwardmethod, the gradients will be accumulated to.gradproperties instead of replacement. If the gradient is not cleared, the gradient calculated in the next iteration will be superimposed with the gradient of this iteration, causing an error.
Note: The above code is a complete training step. In actual use, it is usually necessary to put this process into a loop and perform multiple iterations on the entire training data set until the model performance meets the requirements or reaches the preset maximum number of iterations. In each iteration, you can also add some code to record the training progress, such as printing the current loss value, or evaluating the performance of the model on the validation data set.
The complete code is as follows:
1 | import torch |
The above is a basic introduction to the backpropagation algorithm, which plays a very important role in deep learning training and is the basic knowledge for understanding and implementing neural networks.
Python handwritten backpropagation
The following is a simple backpropagation algorithm Python code example with a detailed step-by-step explanation:
Set random number seed and data:
1
2
3np.random.seed(0)
X = np.array([[0,0,1],[0,1,1],[1,0,1],[1,1,1]])
y = np.array([[0,1,1,0]]).TThis code first sets a random number seed to ensure that the initialized weight value is the same every time the program is run. Next, we set the input data
Xand output datay。Define Sigmoid function:
1
2
3
4def sigmoid(x, deriv=False):
if deriv:
return x*(1-x)
return 1/(1+np.exp(-x))Here we define the Sigmoid function, which is used to activate the output of the neuron. in parameters
derivWhen True, this function returns the derivative of the Sigmoid function, which is important for backpropagation to calculate the gradient.Initialize weights:
1
2w0 = 2*np.random.random((3,4)) - 1
w1 = 2*np.random.random((4,1)) - 1Here we initialize the weights
w0andw1. Our network has two layers, so we need two sets of weights. The initialization values of these weights are randomly chosen between -1 and 1.iterative training:
1
for j in range(60000):
This is our training loop, we train the network 60,000 times.
forward propagation:
1
2
3l0 = X
l1 = sigmoid(np.dot(l0, w0))
l2 = sigmoid(np.dot(l1, w1))This is our forward propagation step. First, our input
l0It's dataX. Then, we calculatel1layer, this isl0andw0The dot product is the result of the Sigmoid function. Likewise, we calculatel2, which isl1andw1The dot product is the result of the Sigmoid function.Calculation error:
1
l2_loss = y - l2
Here we calculate the error predicted by the network and this is the actual value
ySubtract predicted valuel2。printing error:
1
2if j % 10000 == 0:
print(f'Loss: {np.mean(np.abs(l2_loss))}')Every 10,000 iterations, we calculate and print the average error.
Backpropagation:
1
2
3l2_delta = l2_loss * sigmoid(l2, deriv=True)
l1_loss = l2_delta.dot(w1.T)
l1_delta = l1_loss * sigmoid(l1, deriv=True)This is the step of backpropagation. We first calculate
l2_delta, which isl2_lossandl2The result of the Sigmoid derivative function. Then, we calculatel1_loss, which isl2_deltaandw1The dot product of the transpose of . Finally, we calculatel1_delta, which isl1_lossandl1The result of the Sigmoid derivative function.Update weights:
1
2w1 += l1.T.dot(l2_delta)
w0 += l0.T.dot(l1_delta)Here, we update the weights based on the results of backpropagation.
w1The increase isl1The transposed sum ofl2_deltaThe dot product ofw0The increase isl0The transposed sum ofl1_deltadot product.
This is the entire network training process, including forward propagation, calculating errors, back propagation and updating weights.