• 算法特征
    ①. 统一看待线性运算与非线性运算; ②. 确定求导变量loss影响链路; ③. loss影响链路梯度逐级反向传播.
  • 算法推导
    Part Ⅰ
    以如下简单正向传播链为例, 引入线性运算与非线性运算符号,

    相关运算流程如下,
    $$
    \begin{equation*}
    \begin{split}
    &\text{linear operation } & I^{(l+1)} = W^{(l)}\cdot O^{(l)} + b^{(l)} \\
    &\text{non-linear operation }\quad & O^{(l+1)} = f^{(l+1)}(I^{(l+1)})
    \end{split}
    \end{equation*}
    $$
    其中, $O^{(l)}$、$I^{(l+1)}$、$O^{(l+1)}$分别为第$l$层输出(output)、第$l+1$层输入(input)、第$l+1$层输出, $W^{(l)}$、$b^{(l)}$、$f^{(l+1)}$分别为相关weight、bias及activation function. 对于线性运算, bias可合并至weight, 则
    $$
    \begin{equation*}
    \text{linear operation }\quad I^{(l+1)} = \tilde{W}^{(l)}\cdot \tilde{O}^{(l)} = g^{(l)}(\tilde{O}^{(l)})
    \end{equation*}
    $$
    其中, $\tilde{W}^{(l)} = [W^{(l)\mathrm{T}}, b^{(l)}]^\mathrm{T}$, $\tilde{O}^{(l)}=[O^{(l)\mathrm{T}},1]^\mathrm{T}$.
    对于上述简单正向传播链, 基础影响链路如下,

    $$
    \begin{equation*}
    \begin{split}
    &\tilde{O}^{(l)} &\quad\rightarrow\quad I^{(l+1)} \\
    &\tilde{W}^{(l)} &\quad\rightarrow\quad I^{(l+1)} \\
    &I^{(l+1)} &\quad\rightarrow\quad O^{(l+1)}
    \end{split}
    \end{equation*}
    $$
    根据链式法则, 影响链路起点由求导变量决定, 终点位于Loss处.
    Part Ⅱ
    现以如下神经网络为例, 加以阐述,

    其中, $(x_1, x_2)$为网络输入, $(y_1, y_2)$为网络输出. 统一处理其中线性运算与非线性运算, 并将该网络完全展开,

    现以$w_1^{(0)}$为例, 作为求导变量确定loss影响链路. $w_1^{(0)}$之loss影响链路(红色+绿色箭头)如下,

    拆解为基础影响链路, 如下,
    $$
    \begin{equation*}
    \begin{split}
    & w_1^{(0)} &\quad\rightarrow\quad I_1^{(1)} \\
    & I_1^{(1)} &\quad\rightarrow\quad O_1^{(1)} \\
    & O_1^{(1)} &\quad\rightarrow\quad I_1^{(2)} \\
    & O_1^{(1)} &\quad\rightarrow\quad I_2^{(2)} \\
    & I_1^{(2)} &\quad\rightarrow\quad y_1 \\
    & I_2^{(2)} &\quad\rightarrow\quad y_2 \\
    & y_1 &\quad\rightarrow\quad L \\
    & y_2 &\quad\rightarrow\quad L
    \end{split}
    \end{equation*}
    $$
    定义源(source)为存在多个下游分支的节点(如: $O_1^{(1)}$), 定义汇(sink)为存在多个上游分支的节点(如: $L$). 根据链式求导法则, 在影响链路上, 下游变量对源变量求导需要在源变量处求和, 汇变量对上游变量求导无需特殊处理, 即,

    $$
    \begin{equation*}
    \begin{split}
    &\text{source variable $x$: }\quad &\frac{\partial J(u(x), v(x))}{\partial x} &= \frac{\partial J(u(x), v(x))}{\partial u}\cdot\frac{\partial u}{\partial x} + \frac{\partial J(u(x), v(x))}{\partial v}\cdot\frac{\partial v}{\partial x} \\
    &\text{sink variable $J$: }\quad &\frac{\partial J(u(x), v(y))}{\partial x} &= \frac{\partial J(u(x), v(y))}{\partial u}\cdot\frac{\partial u}{\partial x}
    \end{split}
    \end{equation*}
    $$
    由此, 根据梯度沿影响链路的反向传播, 可确定loss对$w_1^{(0)}$之偏导,

    $$
    \begin{equation*}
    \frac{\partial L}{\partial w_1^{(0)}} = \left(\frac{\partial L}{\partial y_1}\cdot\frac{\partial y_1}{\partial I_1^{(2)}}\cdot\frac{\partial I_1^{(2)}}{\partial O_1^{(1)}} + \frac{\partial L}{\partial y_2}\cdot\frac{\partial y_2}{\partial I_2^{(2)}}\cdot\frac{\partial I_2^{(2)}}{\partial O_1^{(1)}}\right)\cdot\frac{\partial O_1^{(1)}}{\partial I_1^{(1)}}\cdot\frac{\partial I_1^{(1)}}{\partial w_1^{(0)}}
    \end{equation*}
    $$
    再以$b_2^{(1)}$为例, 作为求导变量确定loss影响链路. $b_2^{(1)}$之loss影响链路(红色+绿色箭头)如下,

    拆解为基础影响链路, 如下,
    $$
    \begin{equation*}
    \begin{split}
    & b_2^{(1)} &\quad\rightarrow\quad I_2^{(2)} \\
    & I_2^{(2)} &\quad\rightarrow\quad y_2 \\
    & y_2 &\quad\rightarrow\quad L
    \end{split}
    \end{equation*}
    $$
    同样, 根据梯度沿影响链的反向传播, 可确定loss对$b_2^{(1)}$之偏导,

    $$
    \begin{equation*}
    \frac{\partial L}{\partial b_2^{(1)}} = \frac{\partial L}{\partial y_2}\cdot\frac{\partial y_2}{\partial I_2^{(2)}}\cdot\frac{\partial I_2^{(2)}}{\partial b_2^{(1)}}
    \end{equation*}
    $$

  • 代码实现
    现以如下简单feed-forward网络为例进行算法实施,

    输入层为$(r, g, b)$, 输出层为$(x,y,lv)$且不取激活函数, 中间隐藏层取激活函数为双曲正切函数$\tanh$. 采用如下损失函数,
    $$
    \begin{equation*}
    L = \sum_i\frac{1}{2}(\bar{x}^{(i)}-x^{(i)})^2 + \frac{1}{2}(\bar{y}^{(i)} - y^{(i)})^2 + \frac{1}{2}(\bar{lv}^{(i)} - lv^{(i)})^2
    \end{equation*}
    $$
    其中, $i$为data序号, $(\bar{x}, \bar{y}, \bar{lv})$为相应观测值. 相关training data采用如下策略生成,

    $$
    \begin{equation*}
    \left\{
    \begin{split}
    x &= r + 2g + 3b \\
    y &= r^2 + 2g^2 + 3b^2 \\
    lv &= -3r - 4g - 5b
    \end{split}
    \right.
    \end{equation*}
    $$
    具体实现如下,

      1 # Back Propagation之实现
    2 # 优化器使用Adam
    3
    4 import numpy
    5 from matplotlib import pyplot as plt
    6
    7
    8 numpy.random.seed(1)
    9
    10
    11 # 生成training data
    12 def getData(n=100):
    13 rgbRange = (-1, 1)
    14 r = numpy.random.uniform(*rgbRange, (n, 1))
    15 g = numpy.random.uniform(*rgbRange, (n, 1))
    16 b = numpy.random.uniform(*rgbRange, (n, 1))
    17 x_ = r + 2 * g + 3 * b
    18 y_ = r ** 2 + 2 * g ** 2 + 3 * b ** 2
    19 lv_ = -3 * r - 4 * g - 5 * b
    20 RGB = numpy.hstack((r, g, b))
    21 XYLv_ = numpy.hstack((x_, y_, lv_))
    22 return RGB, XYLv_
    23
    24
    25 class BPEx(object):
    26
    27 def __init__(self, RGB, XYLv_):
    28 self.__RGB = RGB
    29 self.__XYLv_ = XYLv_
    30
    31 self.__rgb = None # (1, 3)
    32 self.__O0 = None # (1, 4)
    33 self.__W0 = None # (4, 5)
    34 self.__I1 = None # (1, 5)
    35 self.__O1 = None # (1, 6)
    36 self.__W1 = None # (6, 3)
    37 self.__I2 = None # (1, 3)
    38 self.__xylv_ = None # (1, 3)
    39 self.__L = None # scalar
    40
    41 self.__grad_W0_I1 = None # 基础影响链路(W0->I1)之梯度
    42 self.__grad_I1_O1 = None # 基础影响链路(I1->O1)之梯度
    43 self.__grad_O1_I2 = None # 基础影响链路(O1->I2)之梯度
    44 self.__grad_W1_I2 = None # 基础影响链路(W1->I2)之梯度
    45 self.__grad_I2_L = None # 基础影响链路(I2->Loss)之梯度
    46
    47 self.__gradList_W0_I1 = list()
    48 self.__gradList_I1_O1 = list()
    49 self.__gradList_O1_I2 = list()
    50 self.__gradList_W1_I2 = list()
    51 self.__gradList_I2_L = list()
    52
    53 self.__init_weights()
    54 self.__bpTag = False # 是否反向传播之标志
    55
    56
    57 def calc_xylv(self, rgb, W0=None, W1=None):
    58 '''
    59 默认在当前W0、W1之基础上进行预测, 实际使用需要配合优化器
    60 '''
    61 if W0 is not None:
    62 self.__W0 = W0
    63 if W1 is not None:
    64 self.__W1 = W1
    65
    66 self.__calc_xylv(numpy.array(rgb).reshape((1, 3)))
    67 xylv = self.__I2
    68 return xylv
    69
    70
    71 def get_W0W1(self):
    72 return self.__W0, self.__W1
    73
    74
    75 def calc_JVal(self, W):
    76 self.__bpTag = True
    77 self.__clr_gradList()
    78
    79 self.__W0 = W[:20, 0].reshape((4, 5))
    80 self.__W1 = W[20:, 0].reshape((6, 3))
    81
    82 JVal = 0
    83 for rgb, xylv_ in zip(self.__RGB, self.__XYLv_):
    84 self.__calc_xylv(rgb.reshape((1, 3)))
    85 self.__calc_loss(xylv_.reshape((1, 3)))
    86 JVal += self.__L
    87 self.__add_gradList()
    88
    89 self.__bpTag = False
    90 return JVal
    91
    92
    93 def calc_grad(self, W):
    94 '''
    95 此处W仅为统一调用接口
    96 '''
    97 grad_W0 = numpy.zeros(self.__W0.shape)
    98 grad_W1 = numpy.zeros(self.__W1.shape)
    99 for grad_W0_I1, grad_I1_O1, grad_O1_I2, grad_W1_I2, grad_I2_L in zip(self.__gradList_W0_I1,\
    100 self.__gradList_I1_O1, self.__gradList_O1_I2, self.__gradList_W1_I2, self.__gradList_I2_L):
    101 grad_W1_curr = grad_I2_L * grad_W1_I2
    102 grad_W1 += grad_W1_curr
    103
    104 term0 = numpy.sum(grad_I2_L.T * grad_O1_I2, axis=0) # (1, 5) source
    105 term1 = term0 * grad_I1_O1
    106 grad_W0_curr = term1 * grad_W0_I1
    107 grad_W0 += grad_W0_curr
    108
    109 grad = numpy.vstack((grad_W0.reshape((-1, 1)), grad_W1.reshape((-1, 1))))
    110 return grad
    111
    112
    113 def init_seed(self):
    114 n = self.__W0.size + self.__W1.size
    115 seed = numpy.random.uniform(-1, 1, (n, 1))
    116 return seed
    117
    118
    119 def __add_gradList(self):
    120 self.__gradList_W0_I1.append(self.__grad_W0_I1)
    121 self.__gradList_I1_O1.append(self.__grad_I1_O1)
    122 self.__gradList_O1_I2.append(self.__grad_O1_I2)
    123 self.__gradList_W1_I2.append(self.__grad_W1_I2)
    124 self.__gradList_I2_L.append(self.__grad_I2_L)
    125
    126
    127 def __clr_gradList(self):
    128 self.__gradList_W0_I1.clear()
    129 self.__gradList_I1_O1.clear()
    130 self.__gradList_O1_I2.clear()
    131 self.__gradList_W1_I2.clear()
    132 self.__gradList_I2_L.clear()
    133
    134
    135 def __init_weights(self):
    136 self.__W0 = numpy.zeros((4, 5))
    137 self.__W1 = numpy.zeros((6, 3))
    138
    139
    140 def __update_grad_by_calc_xylv(self):
    141 self.__grad_W0_I1 = numpy.tile(self.__O0.T, (1, 5))
    142 self.__grad_I1_O1 = 1 - self.__I1_tanh ** 2
    143 self.__grad_O1_I2 = self.__W1.T[:, :-1]
    144 self.__grad_W1_I2 = numpy.tile(self.__O1.T, (1, 3))
    145
    146
    147 def __calc_xylv(self, rgb):
    148 '''
    149 rgb: shape=(1, 3)之numpy array
    150 '''
    151 self.__rgb = rgb
    152 self.__O0 = numpy.hstack((self.__rgb, [[1]]))
    153 self.__I1 = numpy.matmul(self.__O0, self.__W0)
    154 self.__I1_tanh = numpy.tanh(self.__I1)
    155 self.__O1 = numpy.hstack((self.__I1_tanh, [[1]]))
    156 self.__I2 = numpy.matmul(self.__O1, self.__W1)
    157
    158 if self.__bpTag:
    159 self.__update_grad_by_calc_xylv()
    160
    161
    162 def __update_grad_by_calc_loss(self):
    163 self.__grad_I2_L = self.__I2 - self.__xylv_
    164
    165
    166 def __calc_loss(self, xylv_):
    167 '''
    168 xylv_: shape=(1, 3)之numpy array
    169 '''
    170 self.__xylv_ = xylv_
    171 self.__L = numpy.sum((self.__xylv_ - self.__I2) ** 2) / 2
    172
    173 if self.__bpTag:
    174 self.__update_grad_by_calc_loss()
    175
    176
    177 class Adam(object):
    178
    179 def __init__(self, _func, _grad, _seed):
    180 '''
    181 _func: 待优化目标函数
    182 _grad: 待优化目标函数之梯度
    183 _seed: 迭代起始点
    184 '''
    185 self.__func = _func
    186 self.__grad = _grad
    187 self.__seed = _seed
    188
    189 self.__xPath = list()
    190 self.__JPath = list()
    191
    192
    193 def get_solu(self, alpha=0.001, beta1=0.9, beta2=0.999, epsilon=1.e-8, zeta=1.e-6, maxIter=3000000):
    194 '''
    195 获取数值解,
    196 alpha: 步长参数
    197 beta1: 一阶矩指数衰减率
    198 beta2: 二阶矩指数衰减率
    199 epsilon: 足够小正数
    200 zeta: 收敛判据
    201 maxIter: 最大迭代次数
    202 '''
    203 self.__init_path()
    204
    205 x = self.__init_x()
    206 JVal = self.__calc_JVal(x)
    207 self.__add_path(x, JVal)
    208 grad = self.__calc_grad(x)
    209 m, v = numpy.zeros(x.shape), numpy.zeros(x.shape)
    210 for k in range(1, maxIter + 1):
    211 print("iterCnt: {:3d}, JVal: {}".format(k, JVal))
    212 if self.__converged1(grad, zeta):
    213 self.__print_MSG(x, JVal, k)
    214 return x, JVal, True
    215
    216 m = beta1 * m + (1 - beta1) * grad
    217 v = beta2 * v + (1 - beta2) * grad * grad
    218 m_ = m / (1 - beta1 ** k)
    219 v_ = v / (1 - beta2 ** k)
    220
    221 alpha_ = alpha / (numpy.sqrt(v_) + epsilon)
    222 d = -m_
    223 xNew = x + alpha_ * d
    224 JNew = self.__calc_JVal(xNew)
    225 self.__add_path(xNew, JNew)
    226 if self.__converged2(xNew - x, JNew - JVal, zeta ** 2):
    227 self.__print_MSG(xNew, JNew, k + 1)
    228 return xNew, JNew, True
    229
    230 gNew = self.__calc_grad(xNew)
    231 x, JVal, grad = xNew, JNew, gNew
    232 else:
    233 if self.__converged1(grad, zeta):
    234 self.__print_MSG(x, JVal, maxIter)
    235 return x, JVal, True
    236
    237 print("Adam not converged after {} steps!".format(maxIter))
    238 return x, JVal, False
    239
    240
    241 def get_path(self):
    242 return self.__xPath, self.__JPath
    243
    244
    245 def __converged1(self, grad, epsilon):
    246 if numpy.linalg.norm(grad, ord=numpy.inf) < epsilon:
    247 return True
    248 return False
    249
    250
    251 def __converged2(self, xDelta, JDelta, epsilon):
    252 val1 = numpy.linalg.norm(xDelta, ord=numpy.inf)
    253 val2 = numpy.abs(JDelta)
    254 if val1 < epsilon or val2 < epsilon:
    255 return True
    256 return False
    257
    258
    259 def __print_MSG(self, x, JVal, iterCnt):
    260 print("Iteration steps: {}".format(iterCnt))
    261 print("Solution:\n{}".format(x.flatten()))
    262 print("JVal: {}".format(JVal))
    263
    264
    265 def __calc_JVal(self, x):
    266 return self.__func(x)
    267
    268
    269 def __calc_grad(self, x):
    270 return self.__grad(x)
    271
    272
    273 def __init_x(self):
    274 return self.__seed
    275
    276
    277 def __init_path(self):
    278 self.__xPath.clear()
    279 self.__JPath.clear()
    280
    281
    282 def __add_path(self, x, JVal):
    283 self.__xPath.append(x)
    284 self.__JPath.append(JVal)
    285
    286
    287 class BPExPlot(object):
    288
    289 @staticmethod
    290 def plot_fig(adamObj):
    291 alpha = 0.001
    292 epoch = 50000
    293 x, JVal, tab = adamObj.get_solu(alpha=alpha, maxIter=epoch)
    294 xPath, JPath = adamObj.get_path()
    295
    296 fig = plt.figure(figsize=(6, 4))
    297 ax1 = fig.add_subplot(1, 1, 1)
    298
    299 ax1.plot(numpy.arange(len(JPath)), JPath, "k.", markersize=1)
    300 ax1.plot(0, JPath[0], "go", label="seed")
    301 ax1.plot(len(JPath)-1, JPath[-1], "r*", label="solution")
    302
    303 ax1.legend()
    304 ax1.set(xlabel="$epoch$", ylabel="$JVal$", title="solution-JVal = {:.5f}".format(JPath[-1]))
    305
    306 fig.tight_layout()
    307 # plt.show()
    308 fig.savefig("plot_fig.png", dpi=100)
    309
    310
    311
    312 if __name__ == "__main__":
    313 RGB, XYLv_ = getData(1000)
    314 bpObj = BPEx(RGB, XYLv_)
    315
    316 # rgb = (0.5, 0.6, 0.7)
    317 # xylv = bpObj.calc_xylv(rgb)
    318 # print(rgb)
    319 # print(xylv)
    320 # func = bpObj.calc_JVal
    321 # grad = bpObj.calc_grad
    322 # seed = bpObj.init_seed()
    323 # adamObj = Adam(func, grad, seed)
    324 # alpha = 0.1
    325 # epoch = 1000
    326 # adamObj.get_solu(alpha=alpha, maxIter=epoch)
    327 # xylv = bpObj.calc_xylv(rgb)
    328 # print(rgb)
    329 # print(xylv)
    330 # W0, W1 = bpObj.get_W0W1()
    331 # xylv = bpObj.calc_xylv(rgb, W0, W1)
    332 # print(rgb)
    333 # print(xylv)
    334
    335 func = bpObj.calc_JVal
    336 grad = bpObj.calc_grad
    337 seed = bpObj.init_seed()
    338 adamObj = Adam(func, grad, seed)
    339 BPExPlot.plot_fig(adamObj)
  • 结果展示

    可以看到, 在training data上总体loss随epoch增加逐渐降低.

  • 使用建议
    ①. 某层之output节点可能受多个该层之input节点影响(如: softmax激活函数), 此时input节点具备source特性;
    ②. 正向计算过程可确定基础影响链路之梯度, 反向传播过程可串联基础影响链路之梯度;
    ③. 初值的选取对非凸问题的优化比较重要, 权重初值尽量不取全0.
  • 参考文档
    ①. Rumelhart D E, Hinton G E, Williams R J. Learning internal representations by error propagation[R]. California Univ San Diego La Jolla Inst for Cognitive Science, 1985.
    ②. Adam (1) - Python实现

Back Propagation - Python实现的更多相关文章

  1. 标签传播算法(Label Propagation)及Python实现

    众所周知,机器学习可以大体分为三大类:监督学习.非监督学习和半监督学习.监督学习可以认为是我们有非常多的labeled标注数据来train一个模型,期待这个模型能学习到数据的分布,以期对未来没有见到的 ...

  2. [Python] 机器学习库资料汇总

    声明:以下内容转载自平行宇宙. Python在科学计算领域,有两个重要的扩展模块:Numpy和Scipy.其中Numpy是一个用python实现的科学计算包.包括: 一个强大的N维数组对象Array: ...

  3. 【转帖】Python在大数据分析及机器学习中的兵器谱

    Flask:Python系的轻量级Web框架. 1. 网页爬虫工具集 Scrapy 推荐大牛pluskid早年的一篇文章:<Scrapy 轻松定制网络爬虫> Beautiful Soup ...

  4. python数据挖掘领域工具包

    原文:http://qxde01.blog.163.com/blog/static/67335744201368101922991/ Python在科学计算领域,有两个重要的扩展模块:Numpy和Sc ...

  5. 一个 11 行 Python 代码实现的神经网络

    一个 11 行 Python 代码实现的神经网络 2015/12/02 · 实践项目 · 15 评论· 神经网络 分享到:18 本文由 伯乐在线 - 耶鲁怕冷 翻译,Namco 校稿.未经许可,禁止转 ...

  6. 大数据分析与机器学习领域Python兵器谱

    http://www.thebigdata.cn/JieJueFangAn/13317.html 曾经因为NLTK的缘故开始学习Python,之后渐渐成为我工作中的第一辅助脚本语言,虽然开发语言是C/ ...

  7. BP神经网络求解异或问题(Python实现)

    反向传播算法(Back Propagation)分二步进行,即正向传播和反向传播.这两个过程简述如下: 1.正向传播 输入的样本从输入层经过隐单元一层一层进行处理,传向输出层:在逐层处理的过程中.在输 ...

  8. python 第三方模块 转 https://github.com/masterpy/zwpy_lst

    Chardet,字符编码探测器,可以自动检测文本.网页.xml的编码. colorama,主要用来给文本添加各种颜色,并且非常简单易用. Prettytable,主要用于在终端或浏览器端构建格式化的输 ...

  9. Python 网页爬虫 & 文本处理 & 科学计算 & 机器学习 & 数据挖掘兵器谱(转)

    原文:http://www.52nlp.cn/python-网页爬虫-文本处理-科学计算-机器学习-数据挖掘 曾经因为NLTK的缘故开始学习Python,之后渐渐成为我工作中的第一辅助脚本语言,虽然开 ...

  10. [resource]Python机器学习库

    reference: http://qxde01.blog.163.com/blog/static/67335744201368101922991/ Python在科学计算领域,有两个重要的扩展模块: ...

随机推荐

  1. 亲测有效! Super PhotoCut Pro 超级抠图工具 V2.8.8 for mac 破解版

    亲测有效! Super PhotoCut Pro 超级抠图工具 V2.8.8 for mac  破解版 Super PhotoCut 是一款专业的,非常易于使用的图片抠图工具.它能够准确地覆盖你想要去 ...

  2. 2.5.scrollView和swiper组件的使用

    # scroll-view 可滚动视图区域.用于区域滚动. 需注意在webview渲染的页面中,区域滚动的性能不及页面滚动. 属性说明 属性名 类型 默认值 说明 平台差异说明 scroll-x Bo ...

  3. rosdep update 一直失败问题

    1.排除网络问题 2.增加TIMEOUT的时间: 更改 /usr/lib/python2.7/dist-packages/rosdep2/下的三个文件sources_list.py.gbpdistro ...

  4. 消息传递(news)题解

    代码 #include<cstdio> #include<algorithm> using namespace std; const int N = 200000; int f ...

  5. JZOJ 6904. 【2020.11.28提高组模拟】T3 树上询问(query)

    题目 你有一棵 \(n\) 节点的树 ,回答 \(m\) 个询问,每次询问给你两个整数 \(l,r\) ,问存在多少个整数 \(k\) 使得从 \(l\) 沿着 \(l \to r\) 的简单路径走 ...

  6. MySQL索引的基本理解

    之前一致以为索引就是简单的在原表的数据上加了一些编号,让查询更加快捷.后来发现里面还有更深的知识. 索引用于快速查找具有特定列值的行.如果没有索引,MySQL 必须从第一行开始,然后通读整个表以找到相 ...

  7. 组织炎症水平高的RA患者接受TNF拮抗剂治疗的效果更好

    组织炎症水平高的RA患者接受TNF拮抗剂治疗的效果更好van der Pouw Kraan TC, et al. Ann Rheum Dis. 2008;67(4):563-6.目的:不同患者对TNF ...

  8. GIS若干相关的概念

    投影 常用的投影坐标系: CGCS2000_3_Degree_GK_CM_111E CGCS2000_3_Degree_GK_Zone_37 CGCS2000_GK_Zone_19 Beijing_1 ...

  9. Java 反射概念的引入

    反射是什么 学Java的人都知道类概念,反射技术就是一种控制类的技术,JAVA程序在运行时,通过反射这个技术,能动态的获取到类实例的信息.创建实体类.操作实体类. 反射的功能列表: 获取任意类的名称. ...

  10. 完全机器模拟浏览器操作自动刷网课!不怕被封!!-----python基于selenium实现超星学习通刷视频网课

    (使用过程中有小伙伴反映如果课程的第一章是空白的页面会报错,我当时做的时候是根据我自己的课程,没有第一节是空页面的现象,这个以后有时间我再改一下吧,或者小伙伴自己修改一下也可) 原谅我这个标题党,对叭 ...