文档章节

(五)神经网络入门之构建多层网络

AllenOR灵感
 AllenOR灵感
发布于 2017/09/10 01:28
字数 4036
阅读 0
收藏 0
点赞 0
评论 0

这篇教程是翻译Peter Roelants写的神经网络教程,作者已经授权翻译,这是原文

该教程将介绍如何入门神经网络,一共包含五部分。你可以在以下链接找到完整内容。

多层网络的推广


这部分教程将介绍两部分:

  • 多层网络的泛化
  • 随机梯度下降的最小批处理分析

在这个教程中,我们把前馈神经网络推到任意数量的隐藏层。其中的概念我们都通过矩阵乘法和非线性变换来进行系统的说明。我们通过构建一个由两层隐藏层组成的小型网络去识别手写数字识别,来说明神经网络向多层神经网络的泛化能力。这个神经网络将是通过随机梯度下降算法进行训练。

我们先导入教程需要使用的软件包。

import numpy as np
import matplotlib.pyplot as plt
from sklearn import datasets, cross_validation, metrics
from matplotlib.colors import colorConverter, ListedColormap
import itertools
import collections

手写数字集

在这个教程中,我们使用scikit-learn提供的手写数字集。这个手写数字集包含1797张8*8的图片。在处理中,我们可以把像素铺平,形成一个64维的向量。下图展示了每个数字的图片。注意,这个数据集和MNIST手写数字集是不一样,MNIST是一个大型的数据集,而这个只是一个小型的数据集。

我们会先对这个数据集进行一个预处理,将这个数据集切分成以下几部分:

  • 一个训练集,用于模型的训练。(输入数据:X_train,目标数据:T_train)
  • 一个验证的数据集,用于去评估模型的性能,如果模型在训练数据集上面出现过拟合了,那么可以终止训练了。(输入数据:X_validation,目标数据:T_avlidation)
  • 一个测试数据集,用于最终对模型的测试。(输入数据:X_test,目标数据:T_test)
# load the data from scikit-learn.
digits = datasets.load_digits()

# Load the targets.
# Note that the targets are stored as digits, these need to be 
#  converted to one-hot-encoding for the output sofmax layer.
T = np.zeros((digits.target.shape[0],10))
T[np.arange(len(T)), digits.target] += 1

# Divide the data into a train and test set.
X_train, X_test, T_train, T_test = cross_validation.train_test_split(
    digits.data, T, test_size=0.4)
# Divide the test set into a validation set and final test set.
X_validation, X_test, T_validation, T_test = cross_validation.train_test_split(
    X_test, T_test, test_size=0.5)
# Plot an example of each image.
fig = plt.figure(figsize=(10, 1), dpi=100)
for i in range(10):
    ax = fig.add_subplot(1,10,i+1)
    ax.matshow(digits.images[i], cmap='binary') 
    ax.axis('off')
plt.show()

手写数字

网络层的泛化

第四部分中,我们设计的神经网络通过矩阵相乘实现一个线性转换和一个非线性函数的转换。

在进行非线性函数处理时,我们是对每个神经元进行处理的,这样的好处是可以帮助我们更加容易的进行理解和计算。

我们利用Python classes构造了三个层:

  • 一个线性转换层LinearLayer
  • 一个Logistic函数LogisticLayer
  • 一个softmax函数层SoftmaxOutputLayer

在正向传递时,每个层可以通过get_output函数计算该层的输出结果,这个结果将被下一层作为输入数据进行使用。在反向传递时,每一层的输入的梯度可以通过get_input_grad函数计算得到。如果是最后一层,那么梯度计算方程将利用目标结果进行计算。如果是中间的某一层,那么梯度就是梯度计算函数的输出结果。如果每个层有迭代参数的话,那么可以在get_params_iter函数中实现,并且在get_params_grad函数中按照原来的顺序实现参数的梯度。

注意,在softmax层中,梯度和损失函数的计算将根据输入样本的数量进行计算。也就是说,这将使得梯度与损失函数和样本数量之间是相互独立的,以至于当我们改变批处理的数量时,对别的参数不会产生影响。

# Define the non-linear functions used
def logistic(z): 
    return 1 / (1 + np.exp(-z))

def logistic_deriv(y):  # Derivative of logistic function
    return np.multiply(y, (1 - y))

def softmax(z): 
    return np.exp(z) / np.sum(np.exp(z), axis=1, keepdims=True)
# Define the layers used in this model
class Layer(object):
    """Base class for the different layers.
    Defines base methods and documentation of methods."""

    def get_params_iter(self):
        """Return an iterator over the parameters (if any).
        The iterator has the same order as get_params_grad.
        The elements returned by the iterator are editable in-place."""
        return []

    def get_params_grad(self, X, output_grad):
        """Return a list of gradients over the parameters.
        The list has the same order as the get_params_iter iterator.
        X is the input.
        output_grad is the gradient at the output of this layer.
        """
        return []

    def get_output(self, X):
        """Perform the forward step linear transformation.
        X is the input."""
        pass

    def get_input_grad(self, Y, output_grad=None, T=None):
        """Return the gradient at the inputs of this layer.
        Y is the pre-computed output of this layer (not needed in this case).
        output_grad is the gradient at the output of this layer 
         (gradient at input of next layer).
        Output layer uses targets T to compute the gradient based on the 
         output error instead of output_grad"""
        pass
class LinearLayer(Layer):
    """The linear layer performs a linear transformation to its input."""

    def __init__(self, n_in, n_out):
        """Initialize hidden layer parameters.
        n_in is the number of input variables.
        n_out is the number of output variables."""
        self.W = np.random.randn(n_in, n_out) * 0.1
        self.b = np.zeros(n_out)

    def get_params_iter(self):
        """Return an iterator over the parameters."""
        return itertools.chain(np.nditer(self.W, op_flags=['readwrite']),
                               np.nditer(self.b, op_flags=['readwrite']))

    def get_output(self, X):
        """Perform the forward step linear transformation."""
        return X.dot(self.W) + self.b

    def get_params_grad(self, X, output_grad):
        """Return a list of gradients over the parameters."""
        JW = X.T.dot(output_grad)
        Jb = np.sum(output_grad, axis=0)
        return [g for g in itertools.chain(np.nditer(JW), np.nditer(Jb))]

    def get_input_grad(self, Y, output_grad):
        """Return the gradient at the inputs of this layer."""
        return output_grad.dot(self.W.T)
class LogisticLayer(Layer):
    """The logistic layer applies the logistic function to its inputs."""

    def get_output(self, X):
        """Perform the forward step transformation."""
        return logistic(X)

    def get_input_grad(self, Y, output_grad):
        """Return the gradient at the inputs of this layer."""
        return np.multiply(logistic_deriv(Y), output_grad)
class SoftmaxOutputLayer(Layer):
    """The softmax output layer computes the classification propabilities at the output."""

    def get_output(self, X):
        """Perform the forward step transformation."""
        return softmax(X)

    def get_input_grad(self, Y, T):
        """Return the gradient at the inputs of this layer."""
        return (Y - T) / Y.shape[0]

    def get_cost(self, Y, T):
        """Return the cost at the output of this output layer."""
        return - np.multiply(T, np.log(Y)).sum() / Y.shape[0]

样本模型

接下来的部分,我们会实现设计的各个网络层,以及层与层之间的线性转换,神经元的非线性激活。

在这个教程中,我们使用的样本模型是由两个隐藏层,Logistic函数作为激活函数,最后使用softmax函数作为分类的一个神经网络模型。第一层的隐藏层将输入的数据从64维度降维到20维度。第二层的隐藏层将前一层输入的20维度经过映射之后,还是以20维度输出。最后一层的输出层是一个10维度的分类结果。下图具体描述了这种架构的实现:


样本模型

这个神经网络被表示成一种序列模型,即当前层的输入数据是前一层的输出数据,当前层的输出数据将成为下一层的输入数据。第一层作为序列的第0位,最后一层作为序列的索引最后位置。

# Define a sample model to be trained on the data
hidden_neurons_1 = 20  # Number of neurons in the first hidden-layer
hidden_neurons_2 = 20  # Number of neurons in the second hidden-layer
# Create the model
layers = [] # Define a list of layers
# Add first hidden layer
layers.append(LinearLayer(X_train.shape[1], hidden_neurons_1))
layers.append(LogisticLayer())
# Add second hidden layer
layers.append(LinearLayer(hidden_neurons_1, hidden_neurons_2))
layers.append(LogisticLayer())
# Add output layer
layers.append(LinearLayer(hidden_neurons_2, T_train.shape[1]))
layers.append(SoftmaxOutputLayer())

BP算法

BP算法在正向传播过程和反向传播过程中的具体细节已经在第四部分中进行了详细的解释,如果对此还有疑问,建议再去学习一下。这一部分,我们只单纯实现在多层神经网络中的BP算法。

正向传播过程

在下列代码中,forward_step函数实现了正向传播过程。get_output函数实现了每层的输出结果。这些激活的输出结果被保存在activations列表中。

# Define the forward propagation step as a method.
def forward_step(input_samples, layers):
    """
    Compute and return the forward activation of each layer in layers.
    Input:
        input_samples: A matrix of input samples (each row is an input vector)
        layers: A list of Layers
    Output:
        A list of activations where the activation at each index i+1 corresponds to
        the activation of layer i in layers. activations[0] contains the input samples.  
    """
    activations = [input_samples] # List of layer activations
    # Compute the forward activations for each layer starting from the first
    X = input_samples
    for layer in layers:
        Y = layer.get_output(X)  # Get the output of the current layer
        activations.append(Y)  # Store the output for future processing
        X = activations[-1]  # Set the current input as the activations of the previous layer
    return activations  # Return the activations of each layer

反向传播过程

在反向传播过程中,backward_step函数实现了反向传播过程。反向传播过程的计算是从最后一层开始的。先利用get_input_grad函数得到最初的梯度。然后,利用get_params_grad函数计算每一层的误差函数的梯度,并且把这些梯度保存在一个列表中。

# Define the backward propagation step as a method
def backward_step(activations, targets, layers):
    """
    Perform the backpropagation step over all the layers and return the parameter gradients.
    Input:
        activations: A list of forward step activations where the activation at 
            each index i+1 corresponds to the activation of layer i in layers. 
            activations[0] contains the input samples. 
        targets: The output targets of the output layer.
        layers: A list of Layers corresponding that generated the outputs in activations.
    Output:
        A list of parameter gradients where the gradients at each index corresponds to
        the parameters gradients of the layer at the same index in layers. 
    """
    param_grads = collections.deque()  # List of parameter gradients for each layer
    output_grad = None  # The error gradient at the output of the current layer
    # Propagate the error backwards through all the layers.
    #  Use reversed to iterate backwards over the list of layers.
    for layer in reversed(layers):   
        Y = activations.pop()  # Get the activations of the last layer on the stack
        # Compute the error at the output layer.
        # The output layer error is calculated different then hidden layer error.
        if output_grad is None:
            input_grad = layer.get_input_grad(Y, targets)
        else:  # output_grad is not None (layer is not output layer)
            input_grad = layer.get_input_grad(Y, output_grad)
        # Get the input of this layer (activations of the previous layer)
        X = activations[-1]
        # Compute the layer parameter gradients used to update the parameters
        grads = layer.get_params_grad(X, output_grad)
        param_grads.appendleft(grads)
        # Compute gradient at output of previous layer (input of current layer):
        output_grad = input_grad
    return list(param_grads)  # Return the parameter gradients

梯度检查

正如在第四部分中的分析,我们通过比较数值梯度和反向传播计算的梯度,来分析梯度是否正确。

在代码中,get_params_iter函数实现了得到每一层的参数,并且返回一个所有参数的迭代。get_params_grad函数根据反向传播,得到每一个参数对应的梯度。

# Perform gradient checking
nb_samples_gradientcheck = 10 # Test the gradients on a subset of the data
X_temp = X_train[0:nb_samples_gradientcheck,:]
T_temp = T_train[0:nb_samples_gradientcheck,:]
# Get the parameter gradients with backpropagation
activations = forward_step(X_temp, layers)
param_grads = backward_step(activations, T_temp, layers)

# Set the small change to compute the numerical gradient
eps = 0.0001
# Compute the numerical gradients of the parameters in all layers.
for idx in range(len(layers)):
    layer = layers[idx]
    layer_backprop_grads = param_grads[idx]
    # Compute the numerical gradient for each parameter in the layer
    for p_idx, param in enumerate(layer.get_params_iter()):
        grad_backprop = layer_backprop_grads[p_idx]
        # + eps
        param += eps
        plus_cost = layers[-1].get_cost(forward_step(X_temp, layers)[-1], T_temp)
        # - eps
        param -= 2 * eps
        min_cost = layers[-1].get_cost(forward_step(X_temp, layers)[-1], T_temp)
        # reset param value
        param += eps
        # calculate numerical gradient
        grad_num = (plus_cost - min_cost)/(2*eps)
        # Raise error if the numerical grade is not close to the backprop gradient
        if not np.isclose(grad_num, grad_backprop):
            raise ValueError('Numerical gradient of {:.6f} is not close to the backpropagation gradient of {:.6f}!'.format(float(grad_num), float(grad_backprop)))
print('No gradient errors found')

No gradient errors found

BP算法中的随机梯度下降

这个教程我们使用一个梯度下降的改进版,称为随机梯度下降,来优化我们的损失函数。在一整个训练集上面,随机梯度下降算法只选择一个子集按照负梯度的方向进行更新。这样处理有以下几个好处:第一,在一个大型的训练数据集上面,我们可以节省时间和内存,因为这个算法减少了很多的矩阵操作。第二,增加了训练样本的多样性。

损失函数需要和输入样本的数量之间相互独立,因为在随机梯度算法处理的每一个过程中,样本子集的数量这一信息都被使用了。这也是为什么我们使用损失函授的均方误差,而不是平方误差。

批处理的最小数量

训练样本的子集经常被称之为最小批处理单位。在下面的代码中,我们将最小批处理单位设置成25,并且将输入数据和目标数据打包成一个元祖输入到网络中。

# Create the minibatches
batch_size = 25  # Approximately 25 samples per batch
nb_of_batches = X_train.shape[0] / batch_size  # Number of batches
# Create batches (X,Y) from the training set
XT_batches = zip(
    np.array_split(X_train, nb_of_batches, axis=0),  # X samples
    np.array_split(T_train, nb_of_batches, axis=0))  # Y targets
随机梯度下降算法的更新

在代码中,update_params函数中实现了对每个参数的更新操作。在每一次的迭代中,我们都使用最简单的梯度下降算法来处理参数的更新,即:


其中,μ是学习率。

nb_of_iterations函数实现了,更新操作将会在一整个训练集上面进行多次迭代,每一次迭代都是取最小批处理单位的数据量。在每次全部迭代完之后,模型将会在验证集上面进行测试。如果在验证集上面,经过三次的完全迭代,损失函数的值没有下降,那么我们就认为模型已经过拟合了,需要终止模型的训练。或者经过设置的最大值300次,模型也会被终止训练。所以的损失误差值将会被保存下来,以便后续的分析。

# Define a method to update the parameters
def update_params(layers, param_grads, learning_rate):
    """
    Function to update the parameters of the given layers with the given gradients
    by gradient descent with the given learning rate.
    """
    for layer, layer_backprop_grads in zip(layers, param_grads):
        for param, grad in itertools.izip(layer.get_params_iter(), layer_backprop_grads):
            # The parameter returned by the iterator point to the memory space of
            #  the original layer and can thus be modified inplace.
            param -= learning_rate * grad  # Update each parameter
# Perform backpropagation
# initalize some lists to store the cost for future analysis        
minibatch_costs = []
training_costs = []
validation_costs = []

max_nb_of_iterations = 300  # Train for a maximum of 300 iterations
learning_rate = 0.1  # Gradient descent learning rate

# Train for the maximum number of iterations
for iteration in range(max_nb_of_iterations):
    for X, T in XT_batches:  # For each minibatch sub-iteration
        activations = forward_step(X, layers)  # Get the activations
        minibatch_cost = layers[-1].get_cost(activations[-1], T)  # Get cost
        minibatch_costs.append(minibatch_cost)
        param_grads = backward_step(activations, T, layers)  # Get the gradients
        update_params(layers, param_grads, learning_rate)  # Update the parameters
    # Get full training cost for future analysis (plots)
    activations = forward_step(X_train, layers)
    train_cost = layers[-1].get_cost(activations[-1], T_train)
    training_costs.append(train_cost)
    # Get full validation cost
    activations = forward_step(X_validation, layers)
    validation_cost = layers[-1].get_cost(activations[-1], T_validation)
    validation_costs.append(validation_cost)
    if len(validation_costs) > 3:
        # Stop training if the cost on the validation set doesn't decrease
        #  for 3 iterations
        if validation_costs[-1] >= validation_costs[-2] >= validation_costs[-3]:
            break

nb_of_iterations = iteration + 1  # The number of iterations that have been executed
minibatch_x_inds = np.linspace(0, nb_of_iterations, num=nb_of_iterations*nb_of_batches)
iteration_x_inds = np.linspace(1, nb_of_iterations, num=nb_of_iterations)
# Plot the cost over the iterations
plt.plot(minibatch_x_inds, minibatch_costs, 'k-', linewidth=0.5, label='cost minibatches')
plt.plot(iteration_x_inds, training_costs, 'r-', linewidth=2, label='cost full training set')
plt.plot(iteration_x_inds, validation_costs, 'b-', linewidth=3, label='cost validation set')
# Add labels to the plot
plt.xlabel('iteration')
plt.ylabel('$\\xi$', fontsize=15)
plt.title('Decrease of cost over backprop iteration')
plt.legend()
x1,x2,y1,y2 = plt.axis()
plt.axis((0,nb_of_iterations,0,2.5))
plt.grid()
plt.show()

Descrease of cost over backprop iteration

模型在测试集上面的性能

最后,我们在测试集上面进行模型的最终测试。在这个模型中,我们最后的训练正确率是96%。

最后的结果可以利用混淆图进行更加深入的分析。这个表展示了每一个手写数字被分类为什么数字的数量。下图是利用scikit-learnconfusion_matrix方法实现的。

比如,数字8被误分类了五次,其中,两次被分类成了2,两次被分类成了5,一次被分类成了9。

# Get results of test data
y_true = np.argmax(T_test, axis=1)  # Get the target outputs
activations = forward_step(X_test, layers)  # Get activation of test samples
y_pred = np.argmax(activations[-1], axis=1)  # Get the predictions made by the network
test_accuracy = metrics.accuracy_score(y_true, y_pred)  # Test set accuracy
print('The accuracy on the test set is {:.2f}'.format(test_accuracy))

The accuracy on the test set is 0.96

# Show confusion table
conf_matrix = metrics.confusion_matrix(y_true, y_pred, labels=None)  # Get confustion matrix
# Plot the confusion table
class_names = ['${:d}$'.format(x) for x in range(0, 10)]  # Digit class names
fig = plt.figure()
ax = fig.add_subplot(111)
# Show class labels on each axis
ax.xaxis.tick_top()
major_ticks = range(0,10)
minor_ticks = [x + 0.5 for x in range(0, 10)]
ax.xaxis.set_ticks(major_ticks, minor=False)
ax.yaxis.set_ticks(major_ticks, minor=False)
ax.xaxis.set_ticks(minor_ticks, minor=True)
ax.yaxis.set_ticks(minor_ticks, minor=True)
ax.xaxis.set_ticklabels(class_names, minor=False, fontsize=15)
ax.yaxis.set_ticklabels(class_names, minor=False, fontsize=15)
# Set plot labels
ax.yaxis.set_label_position("right")
ax.set_xlabel('Predicted label')
ax.set_ylabel('True label')
fig.suptitle('Confusion table', y=1.03, fontsize=15)
# Show a grid to seperate digits
ax.grid(b=True, which=u'minor')
# Color each grid cell according to the number classes predicted
ax.imshow(conf_matrix, interpolation='nearest', cmap='binary')
# Show the number of samples in each cell
for x in xrange(conf_matrix.shape[0]):
    for y in xrange(conf_matrix.shape[1]):
        color = 'w' if x == y else 'k'
        ax.text(x, y, conf_matrix[y,x], ha="center", va="center", color=color)       
plt.show()

Confusion table

完整代码,点击这里

本文转载自:http://www.jianshu.com/p/cb6d0d5d777b

共有 人打赏支持
AllenOR灵感
粉丝 10
博文 2139
码字总数 82983
作品 0
程序员
机器学习之softmax函数

Softmax分类函数 这篇教程是翻译Peter Roelants写的神经网络教程,作者已经授权翻译,这是原文。 该教程将介绍如何入门神经网络,一共包含五部分。你可以在以下链接找到完整内容。 (一)神经...

qq_37634812 ⋅ 2017/12/07 ⋅ 0

TensorFlow人工智能引擎入门教程所有目录

TensorFlow 人工智能引擎 入门教程之一 基本概念以及理解 TensorFlow人工智能引擎入门教程之二 CNN卷积神经网络的基本定义理解。 TensorFlow人工智能引擎入门教程之三 实现一个自创的CNN卷积...

zhuyuping ⋅ 2016/04/22 ⋅ 6

深度学习(三):Keras初探:多层感知机

之前使用Keras构建了简单的前馈神经网络,并通过SGD方式进行训练,很好地已完成红酒分类任务。现在我们通过复杂一些的任务来看看BP神经网络的性能。我们选择mnist手写数字库来进行测试,Ker...

monte3card ⋅ 2017/06/12 ⋅ 0

深度学习(二):Keras初探:BP神经网络

最近在学习Keras的使用,在此整理一下相关内容。方式方法上,我想通过使用Keras构建多种常见神经网络来逐步理解Keras,这样的话,通过常见、容易理解的神经网络,一步步深入Keras使用。子曰:...

monte3card ⋅ 2017/06/10 ⋅ 0

神经网络入门指南

人工神经网络(ANN)是一种从信息处理角度对人脑神经元网络进行抽象从而建立的某种简单模型,按不同的连接方式组成不同的网络。其在语音识别、计算机视觉和文本处理等方面取得的突破性成果。...

【方向】 ⋅ 2017/12/02 ⋅ 0

损失函数减肥用,神经网络调权重(深度学习入门系列之六)

更多深度文章,请关注云计算频道:https://yq.aliyun.com/cloud 系列文章: 一入侯门“深”似海,深度学习深几许(深度学习入门系列之一) 人工“碳”索意犹尽,智能“硅”来未可知(深度学习...

玉來愈宏 ⋅ 2017/08/07 ⋅ 0

深度全解卷积神经网络(附论文)

第一章 引言 一、本文动机 过去几年,计算机视觉研究主要集中在卷积神经网络上(通常简称为 ConvNet 或 CNN),在大量诸如分类和回归任务上已经实现了目前为止最佳的表现。尽管这些方法的历史...

技术小能手 ⋅ 05/07 ⋅ 0

Synaptic.js —— 在浏览器上训练 AI 的神经网络

Synaptic.js 可以构建和训练基本上任何类型的一阶甚至二阶神经网络,内置了 4 种经典的神经网络算法:多层感知器(multilayer perceptrons)、长短期记忆网络(multilayer long-short term me...

王练 ⋅ 2017/09/03 ⋅ 0

入门 | 一文了解神经网络中的梯度爆炸

  选自MACHINE LEARNING MASTERY   作者:Jason Brownlee   机器之心编译   参与:路雪、刘晓坤      梯度爆炸指神经网络训练过程中大的误差梯度不断累积,导致模型权重出现重大...

机器之心 ⋅ 2017/12/22 ⋅ 0

用于浏览器的神经网络库--Synaptic.js

Synaptic.js 是一个用于 node.js 和浏览器的 JavaScript 神经网络库,可以构建和训练基本上任何类型的一阶甚至二阶神经网络。 该项目内置了 4 种经典的神经网络算法:多层感知器(multilaye...

匿名 ⋅ 2017/09/01 ⋅ 0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

mysql5.7系列修改root默认密码

操作系统为centos7 64 1、修改 /etc/my.cnf,在 [mysqld] 小节下添加一行:skip-grant-tables=1 这一行配置让 mysqld 启动时不对密码进行验证 2、重启 mysqld 服务:systemctl restart mysql...

sskill ⋅ 24分钟前 ⋅ 0

Intellij IDEA神器常用技巧六-Debug详解

在调试代码的时候,你的项目得debug模式启动,也就是点那个绿色的甲虫启动服务器,然后,就可以在代码里面断点调试啦。下面不要在意,这个快捷键具体是啥,因为,这个keymap是可以自己配置的...

Mkeeper ⋅ 28分钟前 ⋅ 0

zip压缩工具、tar打包、打包并压缩

zip 支持压缩目录 1.在/tmp/目录下创建目录(study_zip)及文件 root@yolks1 study_zip]# !treetree 11└── 2 └── 3 └── test_zip.txt2 directories, 1 file 2.yum...

蛋黄Yolks ⋅ 30分钟前 ⋅ 0

聊聊HystrixThreadPool

序 本文主要研究一下HystrixThreadPool HystrixThreadPool hystrix-core-1.5.12-sources.jar!/com/netflix/hystrix/HystrixThreadPool.java /** * ThreadPool used to executed {@link Hys......

go4it ⋅ 49分钟前 ⋅ 0

容器之上传镜像到Docker hub

Docker hub在国内可以访问,首先要创建一个账号,这个后面会用到,我是用126邮箱注册的。 1. docker login List-1 Username不能使用你注册的邮箱,要用使用注册时用的username;要输入密码 ...

汉斯-冯-拉特 ⋅ 55分钟前 ⋅ 0

SpringBoot简单使用ehcache

1,SpringBoot版本 2.0.3.RELEASE ①,pom.xml <parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.0.3.RELE......

暗中观察 ⋅ 56分钟前 ⋅ 0

监控各项服务

比如有三个服务, 为了减少故障时间,增加监控任务,使用linux的 crontab 实现. 步骤: 1,每个服务写一个ping接口 监控如下内容: 1,HouseServer 是否正常运行,所以需要增加一个ping的接口 ; http...

黄威 ⋅ 今天 ⋅ 0

Spring源码解析(八)——实例创建(下)

前言 来到实例创建的最后一节,前面已经将一个实例通过不同方式(工厂方法、构造器注入、默认构造器)给创建出来了,下面我们要对创建出来的实例进行一些“加工”处理。 源码解读 回顾下之前...

MarvelCode ⋅ 今天 ⋅ 0

nodejs __proto__跟prototype

前言 nodejs中完全没有class的这个概念,这点跟PHP,JAVA等面向对象的语言很不一样,没有class跟object的区分,那么nodejs是怎么样实现继承的呢? 对象 对象是由属性跟方法组成的一个东西,就...

Ai5tbb ⋅ 今天 ⋅ 0

Ubuntu16.04 PHP7.0 不能用MYSQLi方式连接MySQL5.7数据库

Q: Ubuntu16.04 PHP7.0 不能用MYSQLi方式连接MySQL5.7数据库 A: 执行以下2条命令解决: apt-get install php-mysql service apache2 restart php -m 执行后会多以下4个模块: mysqli mysqlnd...

SamXIAO ⋅ 今天 ⋅ 0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

返回顶部
顶部