吴恩达机器学习:神经网络学习和作业
神经网络
- (一)神经网络模型理解
- 1.1 模型
- 1.2 神经网络模型(前馈)
- 1.3 建立神经网络模型
- 1.4 多元分类
- 1.5 循环神经网络与对称连接网络
- (二)神经网络模型实现
- 2.1 代价函数
- 2.2 反向传播
- 2.2.1 数学推导
- (三)神经网络的python实现
- 3.1 神经网络
- 3.2 手写字符识别
- 3.3 用神经网络实现手写字符识别
- 3.4 拓展:改变代价函数
(一)神经网络模型理解
1.1 模型
这是一个三层神经网络,第一层为输入层,第二层为隐藏层,第三层为输出层。
简单模型:
- 线性回归模型
- 逻辑回归模型
优点是容易理解、可解释性较强,但是为了达到相对好的预测效果,可对原始特征进一步抽象,增加更多的特征项来弥补模型在处理非线性问题上的缺陷。
LR是一种广义的线性模型,为了提高LR对非线性问题的处理能力,要引入多项式特征,为了充分考虑特征关联,通常也会执行特征之间交叉组合。若仅考虑二次项,也就是只考虑特征两两组合,这样也会得到接近5000个组合特征,若再考虑三次项,四此项呢?组合特征的数量会越来越庞大,空间会膨胀,项数过多可能会带来的问题:模型容易过度拟合、在处理这些项时计算量过大。
引入神经网络来解决包含大量特征的数据分类问题。
复杂模型:
神经网络,神经网络模型是参考生物神经网络处理问题的模式而建立的一种复杂网络结构。
其缺点就是相对难理解、可解释性不强;优点是这种模型一般不需要像LR那样在特征获取上下这么大的功夫,它可以通过隐层神经元对特征不断抽象,从而自动发觉特征关联,进而使得模型达到良好的表型。
1.2 神经网络模型(前馈)
在神经网络中,神经元又称激活单元,每个激活单元都会采纳大量的特征作为输入,然后根据自身特点提供一个输出,供下层神经元使用。
其中,x0x_{0}x0表示偏置项,它通常作为单元的固有属性而被添加到模型中,一般取值为1; θ\thetaθ是模型参数,也称为权重,起到放大或缩小输入信息的作用;而logistics神经元会将输入信息进行汇总并添加一个非线性变换,从而获得神经元输出。神经网络就是由多个这样的logistics神经元按照不同层次组织起来的网络,每一层的输出都作为下一层的输入,如下图:
第一层为输入层(Input Layer),最后一层为输出层(Output Layer),中间一层为隐藏层(Hidden Layers)。我们为每一层都增加一个偏差单位(bias unit)
1.3 建立神经网络模型
标记:
ai(h)a_{i}^{(h)}ai(h):表示第 h层第 i个神经元的激活值
θji(k)\theta _{ji}^{(k)}θji(k):表示完成从第 k层向第 k+1层映射,所使用的权重矩阵。即层数为 [k,k+1][k,k+1][k,k+1]。该式子中,j:表示第 k+1层第 j个神经元, i:表示第 k层第 i个神经元。
对上述模型,激活单元和输出分别表达为:
把 \small xxx,Θ\ThetaΘ ,aaa 分别用矩阵表示得到前馈神经网络从输入变量获得输出结果的数学表达式:
这样从左到右的算法称为前向传播算法( FORWARD PROPAGATION ))
可以理解神经网络就是,它通过大量的隐藏层网络将输入向量进行一步又一步的抽象(加权求和+非线性变换),生成能够更加容易解释模型的复杂新特征,最后将这些强大的新特征传入输出层获得预测结果。单层神经元(无隐藏层)无法表示逻辑同或运算,但是若加上一个隐藏层结构就可以轻松表示出逻辑同或运算!
1.4 多元分类
对于一张输入图片,需要识别其属于行人、轿车、摩托车或者卡车中的一个类型,就是一个多类分类的问题。神经网络模型也可以处理多分类任务。与二分类不同的是,多分类模型最后的输出层将是一个K维的向量,K表示类别数。用神经网络表示如下:
而用0与1的组合成的向量代替1,2,3,4时,
具体在程序中,可以通过每个输出值取最大值来判断属于哪一类。
1.5 循环神经网络与对称连接网络
- 循环神经网络:
循环网络在他们的连接图中定向了循环,这意味着你可以按照箭头回到你开始的地方。他们可以有复杂的动态,使其很难训练。他们更具有生物真实性。循环神经网路,即一个序列当前的输出与前面的输出也有关。具体的表现形式为网络会对前面的信息进行记忆并应用于当前输出的计算中,即隐藏层之间的节点不再无连接而是有连接的,并且隐藏层的输入不仅包括输入层的输出还包括上一时刻隐藏层的输出。
- 对称连接网络:
对称连接网络有点像循环网络,但是单元之间的连接是对称的(它们在两个方向上权重相同)。比起循环网络,对称连接网络更容易分析。这个网络中有更多的限制,因为它们遵守能量函数定律。没有隐藏单元的对称连接网络被称为“Hopfield 网络”。有隐藏单元的对称连接的网络被称为玻尔兹曼机。
(二)神经网络模型实现
首先需要确定其代价函数,然后对参数进行估计,最后确定模型用于预测。
2.1 代价函数
在分类问题,我们知道使用交叉熵误差函数而不是平方和误差函数,会使得训练速度更快,同时也提升了泛化能力。对于二元分类问题来说,神经网络可以使用单一的logistics神经元作为输出,同时也可以使用两个softmax神经元作为输出。
设神经网络的输出单元有 K个,而网络的第 k个神经元的输出我们用 yk(x,θ)y_{k}(x,\theta )yk(x,θ) 表示,同时其目标用 tkt_{k}tk表示,基于两种不同的考量角度,代价函数由下面给出:
- 有交集的多分类问题
对于此类问题,可以将多分类看成多个相互独立的二元分类问题,每个输出神经元都有两种取值 ( t = 0 , 1 ),并且输出神经元之间相互独立,故给定输入向量时,目标向量的条件概率分布为:
利用似然函数的负对数得误差函数:
- 互斥的多分类问题
对于该问题,我们通常用 “1−of−K”“1-of-K”“1−of−K” 的表示方式来表示类别,从而网络的输出可以表示为yk(x,θ)=P(tk=1∣x)y_{k}(x,\theta )=P(t_{k}=1|x)yk(x,θ)=P(tk=1∣x) ,因此误差函数为:
通常使用Softmax函数计算网络输出 :
yk(x(i))y_{k} (x^{(i)})yk(x(i)),表示第 i个样本第 k个输出单元的输出值,是一个概率值。对于正则化项,依然采用 L2正则项,将所有参数(不包含bias项的参数)的平方和相加,也就是不把 i=0时的参数加进来。
2.2 反向传播
一般的训练算法可以分为两个阶段:
求解代价函数关于权值(参数)的导数。(BP)
用得到的导数进一步计算权值的调整量。(梯度下降等优化算法)
反向传播(BP)算法主要应用第一阶段,非常高效的计算这些导数。
2.2.1 数学推导
假设网络中的每个隐含单元或输入单元都有相同的激活函数 h(⋅)h(\cdot)h(⋅) 。
正则化的代价函数公式:
记,k:输出单元个数
L:神经网络总层数
sls_{l}sl :第L层的单元数(不包括偏置单元)
θ(l)\theta ^{(l)}θ(l) :表示第L层边上的权重即参数
而目标是找出 θ\thetaθ,使得J(θ)J(\theta)J(θ) 的值最小。
于是求偏导 ∂∂θji(l)J(θ)\frac{∂ }{∂θji^{(l)}}J(θ)∂θji(l)∂J(θ)(其中 θji(l)∈R\theta ^{(l)}_{ji}∈Rθji(l)∈R ), J(θ)J(\theta)J(θ) 可以由公式直接给出,关键就是如何计算其偏导。
推导出代价函数导数的计算公式:
首先考虑一个简单的线性模型,其中输出yky_{k}yk 是输入变量xix_{i}xi的线性组合:yk=∑iθkixiyk= \sum_{i}^{}\theta _{ki}xi yk=i∑θkixi
给定一个特定的输入模式n(x,t)n(x, t)n(x,t),则其代价函数为:
En=12∑k(ynk−tnk)2En=\frac{1}{2}\sum_{k}^{}(y_{nk}−t_{nk})^{2} En=21k∑(ynk−tnk)2
则这个代价函数关于参数的梯度为:∂En∂θji=(ynk−tnk)xni\frac{∂ E n}{∂ θ_{j i } } = ( y_{n k} − t_{n k} ) x _{n i } ∂θji∂En=(ynk−tnk)xni
此时代价函数的梯度可以表示为与链接θji\theta _{ji}θji的输出端相关联"误差信号"和与链接输入端相关联的变量xnix_{ni}xni的乘积。
接下来计算神经网络中代价函数关于参数的梯度。首先,因为代价函数中只有加权求和项zjz_{j}zj与参数θji\theta_{ji}θji,故可以通过链式求导法则得到:
∂En∂θji=∂En∂zj(l)∂zj(l)∂θji=∂En∂zj(l)ai(l−1)\frac{∂E_{n}}{∂θ_{ji}}=\frac{∂E_{n}}{∂z_{j}^{(l)}} \frac{∂z_{j}^{(l)}}{∂θ_{ji}}=\frac{∂E_{n}}{∂z_{j}^{(l)}}a_{i}^{(l−1)}∂θji∂En=∂zj(l)∂En∂θji∂zj(l)=∂zj(l)∂Enai(l−1)
引入新的符号:δj=∂En∂zjδ_{j}= \frac{∂E_{n}}{∂z_{j}}δj=∂zj∂En,用其来表示与链接 θji\theta_{ji}θji的输出端相关联"误差(cost)信号",此时代价函数关于参数的梯度可以写成:
∂En∂θji=∂En∂zj(l)ai(l−1)=δj(i)⋅ai(l−1)\frac{∂E_{n}}{∂θ_{ji}}=\frac{∂E_{n}}{∂z_{j}^{(l)}}a_{i}^{(l−1)}=δ_{j}^{(i)}⋅a_{i}^{(l−1)}∂θji∂En=∂zj(l)∂Enai(l−1)=δj(i)⋅ai(l−1)
这个式子得出结论:要求的导数 = 权值 θji\theta_{ji}θji输出端单元的误差项 δj(i)\delta _{j}^{(i)}δj(i) * 权值 θji\theta_{ji}θji输入端单元的激活值 ai(l−1)a_{i}^{(l-1)}ai(l−1)。因为每个结点的激活值在前馈阶段已经得出,因此,为了计算导数,只需要计算网络中每个隐藏层结点和输出结点的"误差(cost)信号"即可。
隐藏层结点和输出结点的"误差(cost)信号"计算:
对于输出结点来说,第k个结点的误差等于该结点的输出值与目标值之间的差:
输出结点(线性激活函数)误差: δk=yk−tkδ_{k}=y_{k}−t_{k}δk=yk−tk
计算隐藏层结点的误差值 δ\deltaδ,再次使用链式法则:
可见, lll层第 j个结点的误差取决于当前结点的激活值、 l+1l+1l+1 层结点的误差、以及 l+1l+1l+1 层结点于当前结点的链接权值。
这种从输出到输入推导误差的方式叫做误差的反向传播。
上述得到的导数只是对于单个样本而言的,因为开始时设定了训练样本只有一个 (x,t);若使用多个训练样本,可以通过加和的形式求的导数: ∂En∂θji=∑n∂En∂θji\frac{\partial E_{n}}{\partial \theta _{ji}} = \sum_{n}^{ }\frac{\partial E_{n}}{\partial \theta _{ji}}∂θji∂En=n∑∂θji∂En
(三)神经网络的python实现
3.1 神经网络
根据sigmoid 函数以及其求导得到对应的python代码:
import numpy as npdef tanh(x):#双曲函数return np.tanh(x)def tanh_deriv(x):"""tanh的导数"""return 1.0 - np.tanh(x) * np.tanh(x)def logistic(x):return 1.0 / (1 + np.exp(-x))def logistic_deriv(x):"""逻辑函数的导数"""fx = logistic(x)return fx * (1 - fx)
神经网络的类结构:
class NeuralNetwork(object):def __init__(self, layers, activation='tanh'):passdef fit(self, X, Y, learning_rate=0.2, epochs=10000):passdef predict(self, x);pass
确定神经网络的层数,每层的个数,从而确定单元间的权重规格和单元的偏向:
def __init__(self, layers, activation='logistic'):""":param layers: 层数,如[4, 3, 2] 表示两层len(list)-1,(因为第一层是输入层,有4个单元),第一层有3个单元,第二层有2个单元:param activation:"""if activation == 'tanh':self.activation = tanhself.activation_deriv = tanh_derivelif activation == 'logistic':self.activation = logisticself.activation_deriv = logistic_deriv# 初始化随机权重self.weights = []for i in range(len(layers) - 1):tmp = (np.random.random([layers[i], layers[i + 1]]) * 2 - 1) * 0.25self.weights.append(tmp)# 偏向随机初始化self.bias = []for i in range(1, len(layers)):self.bias.append((np.random.random(layers[i]) * 2 - 1) * 0.25)
在神经网络的训练中,需要先设定一个训练的终止条件,即达到预设一定的循环次数就停止训练:
def fit(self, X, y, learning_rate=0.2, epochs=10000):X = np.atleast_2d(X)y = np.array(y)# 随即梯度for k in range(epochs):i = np.random.randint(X.shape[0])a = [X[i]] # 随即取某一条实例for j in range(len(self.weights)):a.append(self.activation(np.dot(a[j], self.weights[j]) + self.bias[j] ))errors = y[i] - a[-1]deltas = [errors * self.activation_deriv(a[-1]) ,] # 输出层的误差# 反向传播,对于隐藏层的误差for j in range(len(a) - 2, 0, -1):tmp = np.dot(deltas[-1], self.weights[j].T) * self.activation_deriv(a[j])deltas.append(tmp)deltas.reverse()
每次训练是从样本中随机挑选一个实例进行训练,将这个实例的预测结果和真实结果进行对比,再进行反向传播得到各层的误差,然后再更新权重和偏向。
预测:
#将测试实例从输入层传入,通过正向传播,最后返回输出层的值
def predict(self, row):a = np.array(row) # 确保是 ndarray 对象for i in range(len(self.weights)):a = self.activation(np.dot(a, self.weights[i]) + self.bias[i])return a
3.2 手写字符识别
构造神经网络,并载入数据集:
nn = NeuralNetwork(layers=[64, 100, 10])
from sklearn import datasets
digits = datasets.load_digits()
X = digits.data
y = digits.target
数据集来自 sklearn ,其中由1797个图像组成。神经网络的输入层将有 64 个输入单元,分类结果是 0~9 ,因此输出层有10个单元。
拆分成训练集与数据集,离散化分类结果:
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import LabelBinarizer# 拆分为训练集和测试集
X_train, X_test, y_train, y_test = train_test_split(X, y)# 分类结果离散化
labels_train = LabelBinarizer().fit_transform(y_train)
labels_test = LabelBinarizer().fit_transform(y_test)
训练:
nn.fit(X_train, labels_train)
from sklearn.metrics import confusion_matrix, classification_report
# 收集测试结果
predictions = []
for i in range(X_test.shape[0]):o = nn.predict(X_test[i] )predictions.append(np.argmax(o))# 打印对比结果
print (confusion_matrix(y_test, predictions) )
print (classification_report(y_test, predictions))
实验效果:
当预测值为0,真实值也为0,那么就在 [0][0] 计数 1。因此这个对角线计数越大表示预测越准确:
分类结果:
3.3 用神经网络实现手写字符识别
#载入所需要的包
import matplotlib.pyplot as plt
import numpy as np
import scipy.io as scio #读取.mat文件
载入可视化随机100个数据:
input_layer_size=400 #输入层的单元数 原始输入特征数 20*20=400
hidden_layer_size=25 #隐藏层 25个神经元
num_labels=10 # 10个标签 数字0对应类别10 数字1-9对应类别1-9def display_data(x):(m, n) = x.shape #100*400example_width = np.round(np.sqrt(n)).astype(int) #每个样本显示宽度 round()四舍五入到个位 并转换为intexample_height = (n / example_width).astype(int) #每个样本显示高度 并转换为int#设置显示格式 100个样本 分10行 10列显示display_rows = np.floor(np.sqrt(m)).astype(int)display_cols = np.ceil(m / display_rows).astype(int)# 待显示的每张图片之间的间隔pad = 1# 显示的布局矩阵 初始化值为-1display_array = - np.ones((pad + display_rows * (example_height + pad),pad + display_rows * (example_height + pad)))# Copy each example into a patch on the display arraycurr_ex = 0for j in range(display_rows):for i in range(display_cols):if curr_ex > m:break# Copy the patch# Get the max value of the patchmax_val = np.max(np.abs(x[curr_ex]))display_array[pad + j * (example_height + pad) + np.arange(example_height),pad + i * (example_width + pad) + np.arange(example_width)[:, np.newaxis]] = \x[curr_ex].reshape((example_height, example_width)) / max_valcurr_ex += 1if curr_ex > m:break# 显示图片plt.figure()plt.imshow(display_array, cmap='gray', extent=[-1, 1, -1, 1])plt.axis('off')print('>>Loading and Visualizing Data ')data = scio.loadmat('ex3data1.mat') #读取数据
X = data['X'] #获取输入特征矩阵 5000*400
y = data['y'].flatten() #获取5000个样本的标签 用flatten()函数 将5000*1的2维数组 转换成包含5000个元素的一维数组
m = y.size #样本数 5000# 随机选100个样本 可视化
rand_indices = np.random.permutation(range(m))
selected = X[rand_indices[0:100], :]display_data(selected)
效果:
神经网络参数:
data=scio.loadmat('ex3weights.mat')#读取参数数据
#本实验神经网络结构只有3层 输入层,隐藏层 输出层
theta1 = data['Theta1'] #输入层和隐藏层之间的参数矩阵
theta2 = data['Theta2'] #隐藏层和输出层之间的参数矩阵
经网络的前向传播,实现预测过程:
#sigmoid函数
def sigmoid(z):return 1/(1+np.exp(-z))def predict(theta1,theta2,x):#theta1:25*401 输入层多一个偏置项#theta2:10*26 隐藏层多一个偏置项m=x.shape[0]#样本数num_labels=theta2.shape[0]#类别数x=np.c_[np.ones(m),x] #增加一列1 x:5000*401p=np.zeros(m)z1=x.dot(theta1.T)#z1:5000*25a1=sigmoid(z1) #a1:5000*25a1=np.c_[np.ones(m),a1]#增加一列1 a1:5000*26z2=a1.dot(theta2.T)#z2:5000*10a2=sigmoid(z2)#a2:5000*10p=np.argmax(a2,axis=1)#输出层的10个单元 第一个对应数字1...第十个对应数字0 p+=1 #最大位置+1 即为预测的标签 return ppred = predict(theta1, theta2, X)
print('>>Training set accuracy: {}'.format(np.mean(pred == y)*100))
结果:
Training set accuracry:97.52
3.4 拓展:改变代价函数
正则化是加在cost上的,梯度的正则化是cost函数的正则化的导数。
去掉正则化后过拟合,侧精度值肯定是很高的。本次的正则化按上一次的乘上0.01,不然正则化过头了,精度太低。
#正则化那个式子是人为设置的,感觉想怎么设置就怎么设置?
#去掉正则化后过拟合,侧精度值肯定是很高的。本次的正则化按上一次的乘上0.01,不然正则化过头了,精度太低。def cost(theta, X, y):a1, z2, a2, z3, h = feed_forward(theta, X) J=(y-h.T)**2return J.sum()/len(X)/2def regularized_cost(theta, X, y, l=1):t1, t2 = load_weight('ex4weights.mat')reg = np.sum(t1[:,1:] ** 2) + np.sum(t2[:,1:] ** 2) #正则化的公式在网站上return l / (2 * len(X)) * reg*0.01 + cost(theta, X, y)def gradient(theta, X, y): #不加l=1的话那个优化参数用不了,那个是要求有四个参数。那个的具体参数用法哥不知道。t1, t2 = deserialize(theta) #这里debug了一天,当时忘写了。每次都是要更新t1和t2的,忘了写导致优化速度很快,即少了更新这部a1, z2, a2, z3, h = feed_forward(theta, X)d3 = (h.T - y)*sigmoid_gradient(z3.T) #(5000, 10) #dn是指第n层的误差d2 = np.dot(d3,t2[:,1:] )* sigmoid_gradient(z2.T) #(5000,25)D3=np.dot(a2,d3).T # (10, 26) #D3是指代价函数关于传到第3层的权重的偏导数/改变率。要不要转置,变成权重的维度一样就行D2=np.dot(a1,d2).T # (25, 401)D = (1 / len(X)) * serialize(D2, D3) # (10285,)return Ddef regularized_gradient(theta, X, y, l=1):"""不惩罚偏置单元的参数"""a1, z2, a2, z3, h = feed_forward(theta, X)D2, D3 = deserialize(gradient(theta, X, y))t1[:,0] = 0 #即把偏置单元的参数变成0t2[:,0] = 0reg_D2 = D2 + (l / len(X)) * t1*0.01reg_D3 = D3 + (l / len(X)) * t2*0.01return serialize(reg_D2, reg_D3)def nn_training(X, y):init_theta = random_init(10285) # 25*401 + 10*26res = opt.minimize(fun=regularized_cost,x0=init_theta,args=(X, y, 1),method='TNC',jac=regularized_gradient,options={'maxiter': 400})return resres = nn_training(X, y)def accuracy(theta, X, y):_, _, _, _, h = feed_forward(res.x, X)y_pred = np.argmax(h.T, axis=1) + 1print(classification_report(y, y_pred))accuracy(res.x, X, raw_y)
吴恩达机器学习:神经网络学习和作业相关推荐
- 吴恩达机器学习 -- 神经网络学习
9.1 非线性假设 无论是线性回归还是逻辑回归都有这样一个缺点,即:当特征太多时,计算的负荷会非常大. 吴恩达老师举了一个例子: 当我们使用 的多次项式进行预测时,我们可以应用的很好. 之前课程学习过 ...
- 【吴恩达机器学习】Week4 编程作业ex3——多分类任务和神经网络
Multi-class Classification 1. 数据预处理和可视化 dispalyData.m function [h, display_array] = displayData(X, e ...
- 吴恩达机器学习视频学习笔记
吴恩达机器学习视频笔记 介绍 Introduction 线性回归 Linear Regression 单变量 One Variable 多变量 Multiple Variables 多项式回归 Pol ...
- 吴恩达机器学习/深度学习中文文字版
元学习论文总结||小样本学习论文总结 2017-2019年计算机视觉顶会文章收录 AAAI2017-2019 CVPR2017-2019 ECCV2018 ICCV2017-2019 ICLR2017 ...
- 吴恩达机器学习 神经网络 作业1(用已经求好的权重进行手写数字分类) Python实现 代码详细解释
整个项目的github:https://github.com/RobinLuoNanjing/MachineLearning_Ng_Python 里面可以下载进行代码实现的数据集 题目介绍: In t ...
- 吴恩达卷积神经网络学习笔记(六)|CSDN创作打卡
3.2 特征点检测 神经网络可以通过输出图片上特征点的(x,y)坐标,来实现对目标特征的识别. 我们来看几个例子,假设你正在构建一个人脸识别应用,出于某种原因,你希望算法可以给出眼角的具体位置,眼角坐 ...
- 吴恩达机器学习神经网络作业(python实现)
1. 多分类逻辑回归 自动识别手写数字 import numpy as np import pandas as pd import matplotlib.pyplot as plt from scip ...
- 吴恩达 - 卷积神经网络 学习笔记(一)
转载来源:http://www.cnblogs.com/marsggbo/p/8166487.html DeepLearning.ai学习笔记(四)卷积神经网络 – week1 卷积神经网络基础知识介 ...
- 吴恩达机器学习神经网络 8-1非线性假设
Non-linear hypotheses 非线性假设 神经网络实际上是一个相对古老的算法,但是后来沉寂了一段时间,不过现在,它又成为许多机器学习问题的首选技术. 为什么要研究神经网络? 这是一个监督 ...
- 【吴恩达机器学习】学习笔记——2.1单变量线性回归算法
1 回顾 1.1 监督学习 定义:给定正确答案的机器学习算法 分类: (1)回归算法:预测连续值的输出,如房价的预测 (2)分类算法:离散值的输出,如判断患病是否为某种癌症 1.2 非监督学习 定义: ...
最新文章
- 将二叉树中每一层的节点串成链表
- 随机邻域嵌入_「论文阅读」-学习用于通勤流嵌入的地理上下文嵌入
- GMIS 2017大会邓力演讲:无监督学习的前沿与SPDG方法的优良性
- 【转】android IDE——通过DDMS查看app运行时所占内存情况
- Oracle 中 call 和 exec的区别
- 微信头像单张图片上传
- Dubbo超时机制导致的雪崩连接
- 基于visual Studio2013解决面试题之0702输出数字
- 推荐Java学习书籍
- tf卡测试软件_真正的白菜价?1G不到1元,铠侠(原东芝存储)microSD卡评测
- 解决 pathForResource 返回 nil的问题
- vue+element Form键盘回车事件页面刷新解决
- linux 所有文件大小排序,linux 根据文件大小排序
- LeetCode_88、合并两个数组(python)
- 用Python做出日历
- Pixelmator Pro轻松搞定常见的图片处理需求
- 目前最好的python教程_目和毫米的换算
- vue 中updated的使用
- 5v功放芯片哪个音质好
- Git 各种操作命令详细清单