逻辑回归(LR)算法概述

逻辑回归(Logistic Regression)是用于处理因变量为分类变量的回归问题,常见的是二分类或二项分布问题,也可以处理多分类问题,它实际上是属于一种分类方法。

逻辑回归算法原理

预测函数和决策边界

逻辑回归的预测函数可以表示为:

举一个例子,假设我们有许多样本,并在图中表示出来了,并且假设我们已经通过某种方法求出了LR模型的参数(如下图):

这时,直线上方所有样本都是正样本y=1,直线下方所有样本都是负样本y=0。因此我们可以把这条直线成为决策边界

同理,对于非线性可分的情况,我们只需要引入多项式特征就可以很好的去做分类预测,如下图:

值得注意的一点,决策边界并不是训练集的属性,而是假设本身和参数的属性。因为训练集不可以定义决策边界,它只负责拟合参数;而只有参数确定了,决策边界才得以确定。

代价函数

前面我们介绍线性回归模型时,给出了线性回归的代价函数的形式(误差平方和函数),具体形式如下:

逻辑回归也可以视为一个广义的线性模型,那么线性模型中应用最广泛的代价函数-误差平方和函数,可不可以应用到逻辑回归呢?其实是不可以的,原因在于Sigmoid函数是一个复杂的非线性函数,我们将逻辑回归的假设函数带入上式中,得到J(Θ)是一个非凸函数,如下图:

由上图可知,函数包含多个局部极小值点,使用梯度下降法求解损失函数最小值时,可能导致函数最后结果并非总是全局最小。所以,我们需要为逻辑回归找到一个凸代价函数,最常用的损失函数就是对数损失函数,对数损失函数可以为LR提供一个凸的代价函数,这样有利于使用梯度下降对参数求解。具体公式如下:

对于惩罚函数Cost的两种情况如下图所示:

回顾成本的定义,成本是预测值与真实值的差异。当差异越大时,成本越大,模型受到的“惩罚”也越严重。如上左图所示,当y=1时,随着h(x)的值(预测为1的概率)越来越大,预测值越来越接近真实值,其成本也越来越小。如上右图所示,当y=0时,随着h(x)的值(预测为1的概率)越来越大,预测值越来越偏离真实值,其成本越来越大。

为了可以更加方便的进行后面的参数估计求解,我们合并成一个公式:

因此,我们的代价函数最终形式为:

梯度下降算法-损失函数求解

和线性回归类似,我们使用梯度下降算法来求解逻辑回归模型的参数。代价函数梯度求解过程为:

最终推导出来的梯度下降算法公式为:

正则化

L1正则化和L2正则化可以看做是损失函数的惩罚项。所谓『惩罚』是指对损失函数中的某些参数做一些限制。对于线性回归模型,使用L1正则化的模型建叫做Lasso回归,使用L2正则化的模型叫做Ridge回归(岭回归)
正则化具体解释请参照:机器学习中正则化项L1和L2的直观理解

L1正则化L2正则化的说明如下:

  • L1正则化是指权值向量w中各个元素的绝对值之和,通常表示为:∣∣w∣∣1||w||_1∣∣w∣∣1​
  • L2正则化是指权值向量w中各个元素的平方和然后再求平方根(可以看到Ridge回归的L2正则化项有平方符号),通常表示为:∣∣w∣∣2||w||_2∣∣w∣∣2​

那添加L1和L2正则化有什么用?

  • L1正则化可以产生稀疏权值矩阵(会让模型参数Θ稀疏化,即让模型参数向量里为0的元素尽量多),即产生一个稀疏模型,可以用于特征选择
  • L2正则化会让模型参数尽可能小,但不会为0(即尽量让每个特征对预测值都有一些小的贡献),可以防止模型过拟合(overfitting);一定程度上,L1也可以防止过拟合

因为下文逻辑回归实战中,利用l1作为正则项,这里我讲一下l1作为正则项的用途:

  • 特征选择: 它会让模型参数向量里的元素为0的点尽量多,因此可以排除那些对特征值没有什么影响的特征,从而简化问题。所以l1范数解决过拟合的措施,实际上是减少特征数量。
  • 可解释性:模型参数向量稀疏化后,只会留下那些对特征值有重要影响的特征,这样我们就容易解释模型的因果关系,在下文的乳腺癌检测实战中,可以看到l1作为正则项的作用。

由此可见,l1范数作为正则项,更多的是一个分析工具,适合用来对模型求解,它会把不重要的特征直接去除。大部分情况下,我们解决过拟合问题,还是选择l2范数作为正则项。

算法优缺点

优点

  • 计算代价不高,训练速度较快,容易理解实现。LR在时间和内存需求上相当高效。它可以应用于分布式数据,并且还有在线算法实现,用较少的资源处理大型数据。
  • 模型的可解释性非常好,从特征的权重可以看到不同的特征对最后结果的影响。
  • LR对于数据中小噪声的鲁棒性很好,并且不会受到轻微的多重共线性的特别影响。

缺点

  • 准确率并不是很高。因为形式非常的简单(类似于线性模型),很难去拟合数据的真实分布。
  • 很难处理数据不平衡的问题。
  • 处理非线性数据较麻烦。逻辑回归在不引入其他方法的情况下,只能处理线性可分的数据,或者进一步说,处理二分类的问题 。
  • 对模型中自变量多重共线性较为敏感,需要利用因子分析或者变量聚类分析等手段来选择代表性的自变量,以减少候选变量之间的相关性。

Python实践

scikit-learn中提供了一个LogisticRegression类来实现逻辑回归模型。LogisticRegression

其原型为:sklearn.linear_model.LogisticRegression(penalty=’l2’, dual=False, tol=0.0001, C=1.0, fit_intercept=True, intercept_scaling=1, class_weight=None, random_state=None, solver=’warn’, max_iter=100, multi_class=’warn’, verbose=0, warm_start=False, n_jobs=None)

参数

penaltystr,指定正则化策略。

  • l2l2正则化
  • l1l1正则化

dualbool:如果为True,则求解对偶形式(只在penalty="l2"solver="liblinear"有对偶形式);如果为False,则求解原始形式。
Cfloat,指定罚项系数的倒数。如果它的值越小,则正则化项越大。
fit_interceptbool,指定是否需要计算b值,如果为False,那么不会计算b值(模型会假设数据已经中心化了)
intercept_scalingfloat,只有当solver="liblinear"才有意义。当采用fit_intercept时,相当于人造一个特征出来,该特征恒为1,其权重为b。在计算正则化项的时候,该人造特征也被考虑了。为了降低这个人造特征的影响,需要提供intercept_scaling
class_weightdict或字符串'balanced'

  • 如果为dict:字典给出每个分类的权重,如{class_label: weight}
  • 如果为字符串balanced:则每个分类的权重与该分类在样本集中出现的概率成反比。
  • 如果未指定:每个分类的权重都为1。

max_iterint,指定最大迭代次数。
random_stateint,RandomState实例或None

  • 如果为int:指定随机数生成器的种子。
  • 如果为RandomState实例:指定随机数生成器。
  • 如果为None:使用默认的随机数生成器。

solverstr,指定求解最优化问题的算法。

  • newton-cg:使用牛顿法。
  • lbfgs:使用L-BFGS拟牛顿法。
  • liblinear:使用liblinear
  • sag:使用Stochastic Average Gradient descent算法。
    注意:对于小规模的数据集,liblinear比较适用;对于大规模的数据集,sag比较适用。newton-cglbfgssag只处理penalty=l2的情况。

tolfloat,指定判断迭代收敛与否的阈值。

multi_classstr,指定对于多分类问题的策略。

  • ovr:采用one-vs-rest策略。
  • multinomial:直接采用多分类逻辑回归策略。

verboseint,用于开启/关闭迭代中间输出日志功能。
warm_startbool,如果为True,那么使用前一次训练结果继续训练。否则从头开始训练。
n_jobsint,指定任务并行时的CPU数量。如果为-1则使用所有可用CPU。

属性

coef_:权重向量
intercept_:b值
n_iter_:实际迭代次数

方法

fit(X, y[, sample_weight]):训练模型。
predict(X):用模型进行预测,返回预测值。
predict_log_proba(X):返回一个数组,数组的元素依次是X预测为各个类别的概率的对数值。
predict_proba(X):返回一个数组,数组的元素依次是X预测为各个类别的概率值。
score(X, y[, sample_weight]:返回在(X, y)上预测的准确率。

逻辑回归实战—乳腺癌检测

本文使用逻辑回归算法解决乳腺癌检测问题。scikit-learn中自带一个乳腺癌数据集,为了方便起见,我们直接使用,读者也可直接在网上下载。乳腺癌数据集地址

首先,我们加载数据,输出数据形状和特征。以查看数据:

__author__ = "fpZRobert""""逻辑回归实战—乳腺癌预测
"""from sklearn.datasets import load_breast_cancer
"""加载数据
"""
cancer = load_breast_cancer()
X = cancer.data
y = cancer.target
print("Shape of X: {0}; positive example: {1}; negative: {2}".format(X.shape, y[y==1].shape[0], y[y==0].shape[0]))  # 查看数据的形状和类别分布
print("Cancer data feature name: ", cancer.feature_names)   # 查看数据的特征Out:
Shape of X: (569, 30); positive example: 357; negative: 212
Cancer data feature name:  ['mean radius' 'mean texture' 'mean perimeter' 'mean area' 'mean smoothness' 'mean compactness' 'mean concavity' 'mean concave points' 'mean symmetry' 'mean fractal dimension' 'radius error' 'texture error' 'perimeter error' 'area error' 'smoothness error' 'compactness error' 'concavity error' 'concave points error' 'symmetry error' 'fractal dimension error' 'worst radius' 'worst texture' 'worst perimeter' 'worst area' 'worst smoothness' 'worst compactness' 'worst concavity' 'worst concave points' 'worst symmetry' 'worst fractal dimension']

我们可以看到,数据集总共有569个样本,每个样本有30个特征,其中357个阳性(y=1)样本,212个阴性(y=0)样本。从我们打印出来的特征可以看出,有些指标属于“复合”指标,即由其他的指标经过运算得到的。比如密实度,是由周长和面积计算出来的。这是实际工程应用中常用的特征提取手段:提取特征时,不妨从事务的内在逻辑关系入手,分析已有特征之间的关系,从而构造出新的特征。

回到我们讨论的乳腺癌数据集,实际上它只关注10个特征,然后又构造了每个特征的标准差及最大值,这样每个特征就又衍生出两个特征,所以总共就有了30个特征,这里我们介绍下这10个特征:

特征 含义
radius 半径,即病灶中心点离边界的平均距离
texture 纹理,灰度值的标准偏差
perimeter 周长,即病灶的大小
area 面积,也是反映病灶大小的一个指标
smoothness 平滑度,即半径的变化幅度
compactness 密实度,周长的平方除以面积的商
concavity 凹度,凹陷部分轮廓的严重程度
concave points 凹点,凹陷轮廓的数量
symmetry 对称性
fractal dimension 分形维度

把数据集划分为训练集和测试集(划分比例一般80%用于训练,20%用于测试):

"""构造训练集和测试集
"""
from sklearn.model_selection import train_test_split
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2)

使用逻辑回归算法对数据集进行拟合,并计算训练数据集的评分数据和测试数据集的评分数据:

"""模型训练
"""
import numpy as np
from sklearn.linear_model import LogisticRegressionmodel = LogisticRegression()
model.fit(X_train, y_train)train_score = model.score(X_train, y_train)
test_score = model.score(X_test, y_test)
print("train score: {train_score:.6f}; test score: {test_score:.6f}".format(train_score=train_score, test_score=test_score))Out:
train score: 0.956044; test score: 0.964912

看起来和之前两篇博客的效果不一样,一上来效果就很不错,让我们看一下测试样本中,有哪些是预测正确的:

"""模型预测
"""
y_pred = model.predict(X_test)
print("matchs: {0}/{1}".format(np.equal(y_pred, y_test).shape[0], y_test.shape[0]))Out:
matchs: 114/114

一共114个样本,全部预测正确。这里抛出一个疑问:为什么全部预测正确,但test_score只有0.964912呢?其实,scikit-learn不是使用这个数据来计算分数的,因为这个数据不能完全反映误差情况,而是使用预测概率数据来计算模型得分的。

我们使用LogisticRegression模型的默认参数训练出来的模型,准确性看起来还不错,那么问题又来了,还有优化的空间吗?

首先,我们使用Pipeline来增加多项式特征,跟之前线性回归一样,这里不做过多介绍:

"""模型调优
"""
from sklearn.preprocessing import PolynomialFeatures
from sklearn.pipeline import Pipeline# 构建多项式模型
def polynomial_model(degree=1, **kwarg):polynomial_features = PolynomialFeatures(degree=degree, include_bias=False)logistic_regression = LogisticRegression(**kwarg)pipeline = Pipeline([("polynomial_feature", polynomial_features), ("logistic_regression", logistic_regression)])return pipelinemodel = polynomial_model(degree=2, penalty="l1")
model.fit(X_train, y_train)train_score_v2 = model.score(X_train, y_train)
cv_score_v2 = model.score(X_test, y_test)
print("train_score: {:.6f}, cv_score: {:.6f}".format(train_score_v2, cv_score_v2))Out:
train_score: 0.997802, cv_score: 0.982456

可以看出,训练集评分和测试集评分都增加不少。为什么使用l1范数作为正则项呢?因为l1范数作为正则项,可以实现参数的稀疏化,即自动帮助我们选择那些对模型有关联的特征。我们可以观察一下有多少个特征没有被丢弃:

logistic_regression = model.named_steps["logistic_regression"]
print("model_parameters shape: {0}; count of non-zero element: {1}".format(logistic_regression.coef_.shape,                                                                         np.count_nonzero(logistic_regression.coef_)))Out:
model_parameters shape: (1, 495); count of non-zero element: 92

逻辑回归模型的coef_属性中保存的就是模型参数,从输出结果看,增加到二阶多项式特征后,输入特征由原来的30个增加到495个,最终大多数特征被丢弃,只保留了94个有效特征。

学习曲线是模型最有效的诊断工具之一,首先我们画出L1范数作为正则项所对应的一阶和二阶多项式的学习曲线和L2范数作为正则项所对应的一阶和二阶多项式的学习曲线:

"""绘制学习曲线
"""
import matplotlib.pyplot as plt
from sklearn.model_selection import ShuffleSplit
from sklearn.model_selection import learning_curve# 绘制学习曲线
def plot_learning_curve(plt, estimator, title, X, y, ylim=None, cv=None,n_jobs=1, train_sizes=np.linspace(.1, 1.0, 5)):plt.title(title)if ylim is not None:plt.ylim(*ylim)plt.xlabel("Training examples")plt.ylabel("Score")train_sizes, train_scores, test_scores = learning_curve(estimator, X, y, cv=cv, n_jobs=n_jobs, train_sizes=train_sizes)train_scores_mean = np.mean(train_scores, axis=1)train_scores_std = np.std(train_scores, axis=1)test_scores_mean = np.mean(test_scores, axis=1)test_scores_std = np.std(test_scores, axis=1)plt.grid()plt.fill_between(train_sizes, train_scores_mean - train_scores_std,train_scores_mean + train_scores_std, alpha=0.1,color="r")plt.fill_between(train_sizes, test_scores_mean - test_scores_std,test_scores_mean + test_scores_std, alpha=0.1, color="g")plt.plot(train_sizes, train_scores_mean, 'o--', color="r",label="Training score")plt.plot(train_sizes, test_scores_mean, 'o-', color="g",label="Cross-validation score")plt.legend(loc="best")return plt# 绘制L1范数作为正则化项所对应的一阶和二阶多项式的学习曲线
cv = ShuffleSplit(n_splits=10, test_size=0.2, random_state=0)
title = "Learning Curves (degree={0}, penalty={1})"
degrees = [1, 2]
penalty = "l1"
plt.figure(figsize=(12, 4), dpi=200)
for i in range(len(degrees)):plt.subplot(1, len(degrees), i+1)plot_learning_curve(plt, polynomial_model(degree=degrees[i], penalty=penalty),title.format(degrees[i], penalty), X, y, (0.01, 1.01), cv)
plt.show()# 绘制L2范数作为正则化项所对应的一阶和二阶多项式的学习曲线
penalty = "l2"
plt.figure(figsize=(12, 4), dpi=200)
plt.figure(figsize=(12, 4), dpi=200)
for i in range(len(degrees)):plt.subplot(1, len(degrees), i+1)plot_learning_curve(plt, polynomial_model(degree=degrees[i], penalty=penalty, solver="lbfgs"),title.format(degrees[i], penalty), X, y, (0.01, 1.01), cv)
plt.show()



从上图可以看出,使用多项式并使用L1范数作为正则化项的模型最优,因为它训练样本评分和交叉验证样本评分都很高。但从图中不难发现,训练样本和交叉样本之间的间隙还比较大,可以采集更多的有效数据进一步拟合模型,提高模型预测效果。

全部代码:

__author__ = "fpZRobert""""逻辑回归实战—乳腺癌预测
"""
import numpy as np
import matplotlib.pyplot as plt
from sklearn.model_selection import ShuffleSplit
from sklearn.model_selection import learning_curve
from sklearn.linear_model import LogisticRegression
from sklearn.preprocessing import PolynomialFeatures
from sklearn.pipeline import Pipeline
from sklearn.model_selection import train_test_split
from sklearn.datasets import load_breast_cancer"""加载数据
"""
cancer = load_breast_cancer()
X = cancer.data
y = cancer.target
print("Shape of X: {0}; positive example: {1}; negative: {2}".format(X.shape, y[y==1].shape[0], y[y==0].shape[0]))  # 查看数据的形状和类别分布
print("Boston data feature name: ", cancer.feature_names)   # 查看数据的特征"""构造训练集和测试集
"""
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2)"""模型训练
"""
model = LogisticRegression()
model.fit(X_train, y_train)train_score = model.score(X_train, y_train)
test_score = model.score(X_test, y_test)
print("train score: {train_score:.6f}; test score: {test_score:.6f}".format(train_score=train_score, test_score=test_score))"""模型预测
"""
y_pred = model.predict(X_test)
print("matchs: {0}/{1}".format(np.equal(y_pred, y_test).shape[0], y_test.shape[0]))"""模型调优
"""
# 构建多项式模型
def polynomial_model(degree=1, **kwarg):polynomial_features = PolynomialFeatures(degree=degree, include_bias=False)logistic_regression = LogisticRegression(**kwarg)pipeline = Pipeline([("polynomial_feature", polynomial_features), ("logistic_regression", logistic_regression)])return pipelinemodel = polynomial_model(degree=2, penalty="l1")
model.fit(X_train, y_train)train_score_v2 = model.score(X_train, y_train)
cv_score_v2 = model.score(X_test, y_test)
print("train_score: {:.6f}, cv_score: {:.6f}".format(train_score_v2, cv_score_v2))logistic_regression = model.named_steps["logistic_regression"]
print("model_parameters shape: {0}; count of non-zero element: {1}".format(logistic_regression.coef_.shape,np.count_nonzero(logistic_regression.coef_)))"""绘制学习曲线
"""
# 绘制学习曲线
def plot_learning_curve(plt, estimator, title, X, y, ylim=None, cv=None,n_jobs=1, train_sizes=np.linspace(.1, 1.0, 5)):plt.title(title)if ylim is not None:plt.ylim(*ylim)plt.xlabel("Training examples")plt.ylabel("Score")train_sizes, train_scores, test_scores = learning_curve(estimator, X, y, cv=cv, n_jobs=n_jobs, train_sizes=train_sizes)train_scores_mean = np.mean(train_scores, axis=1)train_scores_std = np.std(train_scores, axis=1)test_scores_mean = np.mean(test_scores, axis=1)test_scores_std = np.std(test_scores, axis=1)plt.grid()plt.fill_between(train_sizes, train_scores_mean - train_scores_std,train_scores_mean + train_scores_std, alpha=0.1,color="r")plt.fill_between(train_sizes, test_scores_mean - test_scores_std,test_scores_mean + test_scores_std, alpha=0.1, color="g")plt.plot(train_sizes, train_scores_mean, 'o--', color="r",label="Training score")plt.plot(train_sizes, test_scores_mean, 'o-', color="g",label="Cross-validation score")plt.legend(loc="best")return plt# 绘制L1范数作为正则化项所对应的一阶和二阶多项式的学习曲线
cv = ShuffleSplit(n_splits=10, test_size=0.2, random_state=0)
title = "Learning Curves (degree={0}, penalty={1})"
degrees = [1, 2]
penalty = "l1"
plt.figure(figsize=(12, 4), dpi=200)
for i in range(len(degrees)):plt.subplot(1, len(degrees), i+1)plot_learning_curve(plt, polynomial_model(degree=degrees[i], penalty=penalty),title.format(degrees[i], penalty), X, y, (0.01, 1.01), cv)
plt.show()# 绘制L2范数作为正则化项所对应的一阶和二阶多项式的学习曲线
penalty = "l2"
plt.figure(figsize=(12, 4), dpi=200)
plt.figure(figsize=(12, 4), dpi=200)
for i in range(len(degrees)):plt.subplot(1, len(degrees), i+1)plot_learning_curve(plt, polynomial_model(degree=degrees[i], penalty=penalty, solver="lbfgs"),title.format(degrees[i], penalty), X, y, (0.01, 1.01), cv)
plt.show()

参考资料

  • 细品 - 逻辑回归(LR)
  • 机器学习中正则化项L1和L2的直观理解

【机器学习算法笔记系列】逻辑回归(LR)算法详解和实战相关推荐

  1. DSML_分类算法笔记之逻辑回归模型

    其他相关文章 DS&ML_关联分析笔记 DS&ML_分类算法笔记之支持自动机SVM模型 DS&ML_分类算法笔记之随机森林.梯度提升树.XGBoost模型 DS&ML_ ...

  2. 唐宇迪机器学习课程笔记:逻辑回归之信用卡检测任务

    信用卡欺诈检测 基于信用卡交易记录数据建立分类模型来预测哪些交易记录是异常的哪些是正常的. 任务流程: 加载数据,观察问题 针对问题给出解决方案 数据集切分 评估方法对比 逻辑回归模型 建模结果分析 ...

  3. 逻辑回归梯度下降法详解

    引言 逻辑回归常用于预测疾病发生的概率,例如因变量是是否恶性肿瘤,自变量是肿瘤的大小.位置.硬度.患者性别.年龄.职业等等(很多文章里举了这个例子,但现代医学发达,可以通过病理检查,即获取标本放到显微 ...

  4. 【数据结构和算法笔记】队列(Queue)详解:c实现

    队列简称队,是一种操作受限的线性表 当rear==MaxSize -1时,队列中可能还有空位置,造成假溢出. 解决办法:环形队列,用取余的方法实现  front为空,rear不为空 链队类型声明:

  5. mllib逻辑回归 spark_Spark架构详解——分布式流计算内存批处理迭代运算框架

    #Spark# #大数据# #Hadoop# #streaming# #分布式计算# Spark:基于内存的高速集群计算框架.对mapreduce进行优化,在内存中进行迭代运算.可以使用Java,Py ...

  6. java基础巩固-宇宙第一AiYWM:为了维持生计,编程语言番外篇之机器学习(项目预测模块总结:线性回归算法、逻辑回归分类算法)~整起

    机器学习 一.机器学习常见算法(未完待续...) 1.算法一:线性回归算法:找一条完美的直线,完美拟合所有的点,使得直线与点的误差最小 2.算法二:逻辑回归分类算法 3.算法三:贝叶斯分类算法 4.算 ...

  7. 吴恩达《机器学习》学习笔记八——逻辑回归(多分类)代码

    吴恩达<机器学习>笔记八--逻辑回归(多分类)代码 导入模块及加载数据 sigmoid函数与假设函数 代价函数 梯度下降 一对多分类 预测验证 课程链接:https://www.bilib ...

  8. 推荐系统(4)-逻辑回归LR、POLY2、FM、FFM

    逻辑回归LR.POLY2.FM.FFM 1.逻辑回归LR 2.辛普森悖论 3.POLY2 4.FM-2010 5.FFM 6.GBDT+LR-2014 7.LS-PLM-2017 <深度学习/推 ...

  9. python机器学习手写算法系列——逻辑回归

    从机器学习到逻辑回归 今天,我们只关注机器学习到线性回归这条线上的概念.别的以后再说.为了让大家听懂,我这次也不查维基百科了,直接按照自己的理解用大白话说,可能不是很严谨. 机器学习就是机器可以自己学 ...

最新文章

  1. XMPP通讯开发-好友获取界面设计
  2. 记一次OutOfMemory定位过程
  3. Getting Started with Node.js LoopBack Framework and IBM Cloudant
  4. C#实现缩放和剪裁图片的方法示例
  5. c语言折半查找输出坐标,数据结构(C语言版)——有序表查找(折半查找)(代码版)...
  6. java 线程执行结束_Java_如何等待子线程执行结束
  7. Perfect swift-server/http
  8. paip.截取字符串byLastDot方法总结uapi python java php c# 总结
  9. mybatis-plus实现自定义字段修改数据 后续更新CRUD
  10. java架构师之路:推荐的15本书
  11. iOS 评论中含有表情的处理方法
  12. 现代大学英语精读第二版(第四册)学习笔记(原文及全文翻译)——5B - Could You Live with Less(你可以简朴地生活吗)
  13. 《说服力》读后总结摘录
  14. linux修改默认22端口失败,【原创文章】修改亚马逊AWS EC2 LINUX系统SSH默认22端口失败的原因和解决办法...
  15. 谷歌404页面html,简洁404页面HTML好看的404错误页源码
  16. 经典 | 单片机常用外围电路设计
  17. Pdf.js body.getReader 报错问题
  18. 如何开启APP2SD功能
  19. python3 分离gif
  20. 针对 store_name 字段

热门文章

  1. 现代控制理论——离散系统,时变系统和非线性系统的状态空间表达式
  2. 【Python】Python抽象类
  3. M2det问题:win10运行错误总结
  4. Neutron VxLAN + Linux Bridge 环境中的网络 MTU
  5. Python selenium 拉钩爬虫
  6. 利用Wireshark截取ICMP数据包,并对数据包进行解析
  7. 影视网站排名,关键词SEO注意事项
  8. 【Linux】linux 快速入门(精讲)
  9. bex5 window bat 自动化更新部署
  10. 计算机网络安全技术(第3版)pdf,《计算机网络安全技术第三章》.pdf