特征真的越多越好吗?从特征工程角度看“garbage in,garbage out”
1. 从朴素贝叶斯在医疗诊断中的迷思说起
这个模型最早被应用于医疗诊断,其中,类变量的不同值用于表示患者可能患的不同疾病。证据变量用于表示不同症状、化验结果等。在简单的疾病诊断上,朴素贝叶斯模型确实发挥了很好的作用,甚至比人类专家的诊断结果都要好。但是在更深度的应用中,医生发现,对于更复杂(由多种致病原因和症状共同表现)的疾病,模型表现的并不好。
数据科学家经过分析认为,出现这种现象的原因在于:模型做了集中通常并不真实的强假设,例如:
- 一个患者至多可能患一种疾病
- 在已知患者的疾病条件下,不同症状的出现与否,不同化验结果,之间是互相独立的
这种模型可用于医学诊断是因为少量可解释的参数易于由专家获得,早期的机器辅助医疗诊断系统正式建立在这一技术之上。
但是,之后更加深入的实践表明,构建这种模型的强假设降低了模型诊断的准确性,尤其是“过度计算”某些特定的证据,该模型很容易过高估计某些方面特征的影响。
例如,“高血压”和“肥胖症”是心脏病的两个硬指标,但是,这两个症状之间相关度很高,高血压一般就伴随着肥胖症。在使用朴素贝叶斯公式计算的时候,由于乘法项的缘故,关于这方面的证据因子就会被重复计算,如下式:
P(心脏病 | 高血压,肥胖症) = P(高血压 | 心脏病) * P(高血压 | 肥胖症) / P(高血压,肥胖症)
由于“高血压”和“肥胖症”之间存在较强相关性的缘故,我们可以很容易想象,分子乘积增加的比率是大于分母联合分布增加的比率的。因此,当分子项继续增加的时候,最终的后验概率就会不断增大。但是因为新增的特征项并没有提供新的信息,后验概率的这种增大变化反而降低了模型的预测性能。
实际上,在实践中人们发现,朴素贝叶斯模型的诊断性能会随着特征的增加而降低,这种降低常常归因于违背了强条件独立性假设。
笔者将这种现象称之为“过度特征化(over-featuring)”,这是工程中常见的一种现象,过度特征化如果无法得到有效规避,会显著降低模型的泛化和预测性能。在这篇文章中,我们通过实验和分析来论证这个说法。
2. 用鸢尾花分类例子讨论特征工程问题
0x1:数据集观察
- 花萼长度
- 花萼宽度
- 花瓣长度
- 花瓣宽度
可以通过这4个特征预测鸢尾花卉属于(iris-setosa, iris-versicolour, iris-virginica)中的哪一品种。
0x2:欠特征化(under-featuring)
我们先来讨论欠特征化(under-featuring)的情况,我们的数据集中有4个维度的特征,并且这4个特征和目标target的相关度都是很高的,换句话说这4个特征都是富含信息量的特征:
# -*- coding: utf- -*- from sklearn.naive_bayes import GaussianNB
import numpy as np
from sklearn.datasets import load_iris
from sklearn.metrics import accuracy_score
from sklearn.metrics import confusion_matrix
import numpy
from sklearn.utils import shuffleif __name__ == '__main__':
# naive Bayes
muNB = GaussianNB() # load data
iris = load_iris()
print "np.shape(iris.data): ", np.shape(iris.data) # feature vec
X_train = iris.data[:int(len(iris.data)*0.8)]
X_test = iris.data[int(len(iris.data)*0.8):]
# label
Y_train = iris.target[:int(len(iris.data)*0.8)]
Y_test = iris.target[int(len(iris.data)*0.8):] # shuffle
X_train, Y_train = shuffle(X_train, Y_train)
X_test, Y_test = shuffle(X_test, Y_test) # load origin feature
X_train_vec = X_train[:, :]
X_test_vec = X_test[:, :] print "Pearson Relevance X[0]: ", numpy.corrcoef(np.array([i[] for i in X_train_vec[:, :]]), Y_train)[, ]
print "Pearson Relevance X[1]: ", numpy.corrcoef(np.array([i[] for i in X_train_vec[:, :]]), Y_train)[, ]
print "Pearson Relevance X[2]: ", numpy.corrcoef(np.array([i[] for i in X_train_vec[:, :]]), Y_train)[, ]
print "Pearson Relevance X[3]: ", numpy.corrcoef(np.array([i[] for i in X_train_vec[:, :]]), Y_train)[, ]
4个特征的皮尔森相关度都超过了0.5
现在我们分别尝试只使用1个、2个、3个、4个特征情况下,训练得到的朴素贝叶斯模型的泛化和预测性能:
# -*- coding: utf- -*- from sklearn.naive_bayes import GaussianNB
import numpy as np
from sklearn.datasets import load_iris
from sklearn.metrics import accuracy_score
from sklearn.metrics import confusion_matrix
import numpy
from sklearn.utils import shuffle def model_tain_and_test(feature_cn):
# load origin feature
X_train_vec = X_train[:, :feature_cn]
X_test_vec = X_test[:, :feature_cn] # train model
muNB.fit(X_train_vec, Y_train)
# predidct the test data
y_predict = muNB.predict(X_test_vec) print "feature_cn: ", feature_cn
print 'accuracy is: {0}'.format(accuracy_score(Y_test, y_predict))
print 'error is: {0}'.format(confusion_matrix(Y_test, y_predict))
print ' ' if __name__ == '__main__':
# naive Bayes
muNB = GaussianNB() # load data
iris = load_iris()
print "np.shape(iris.data): ", np.shape(iris.data) # feature vec
X_train = iris.data[:int(len(iris.data)*0.8)]
X_test = iris.data[int(len(iris.data)*0.8):]
# label
Y_train = iris.target[:int(len(iris.data)*0.8)]
Y_test = iris.target[int(len(iris.data)*0.8):] # shuffle
X_train, Y_train = shuffle(X_train, Y_train)
X_test, Y_test = shuffle(X_test, Y_test) # train and test the generalization and prediction
model_tain_and_test()
model_tain_and_test()
model_tain_and_test()
model_tain_and_test()
可以看到,只使用1个特征的时候,在测试集上的预测精确度只有33.3%,随着特征数的增加,测试集上的预测精确度逐渐增加。
用贝叶斯网的角度来看朴素贝叶斯模型,有如下结构图,
Xi节点这里相当于特征,网络中每个Xi节点的增加,都会改变对Class结果的概率推理,Xi越多,推理的准确度也就越高。
从信息论的角度也很好理解,我们可以将P(Class | Xi)看成是条件熵的信息传递过程,我们提供的信息越多,原则上,对Class的不确定性就会越低。
至此,我们得出如下结论:
特征工程过程中需要特别关注描述完整性问题(description integrity problem),特征维度没有完整的情况下,提供再多的数据对模型效果都没有实质的帮助。样本集的概率完整性要从“特征完整性”和“数据完整性”两个方面保证,它们二者归根结底还是信息完整性的本质问题。
0x3:过特征化(over-featuring)
现在我们在原始的4个特征维度上,继续增加新的无用特征,即那种和目标target相关度很低的特征。
# -*- coding: utf- -*- from sklearn.naive_bayes import GaussianNB
import numpy as np
from sklearn.datasets import load_iris
from sklearn.metrics import accuracy_score
from sklearn.metrics import confusion_matrix
import numpy
from sklearn.utils import shuffle
import random def feature_expend(feature_vec):
# colum_1 * colum_2
feature_vec = np.hstack((feature_vec, np.array([[i] for i in np.multiply(feature_vec[:, ], feature_vec[:, ])])))
# random from colum_1
feature_vec = np.hstack((feature_vec, np.array([[random.uniform(., i)] for i in feature_vec[:, ]])))
feature_vec = np.hstack((feature_vec, np.array([[random.uniform(., i)] for i in feature_vec[:, ]])))
feature_vec = np.hstack((feature_vec, np.array([[random.uniform(., i)] for i in feature_vec[:, ]])))
feature_vec = np.hstack((feature_vec, np.array([[random.uniform(., i)] for i in feature_vec[:, ]])))
# random from colum_2
feature_vec = np.hstack((feature_vec, np.array([[random.uniform(., i)] for i in feature_vec[:, ]])))
feature_vec = np.hstack((feature_vec, np.array([[random.uniform(., i)] for i in feature_vec[:, ]])))
feature_vec = np.hstack((feature_vec, np.array([[random.uniform(., i)] for i in feature_vec[:, ]])))
feature_vec = np.hstack((feature_vec, np.array([[random.uniform(., i)] for i in feature_vec[:, ]]))) return feature_vec def model_tain_and_test(X_train, X_test, Y_train, Y_test, feature_cn):
# load origin feature
X_train_vec = X_train[:, :feature_cn]
X_test_vec = X_test[:, :feature_cn] # train model
muNB.fit(X_train_vec, Y_train)
# predidct the test data
y_predict = muNB.predict(X_test_vec) print "feature_cn: ", feature_cn
print 'accuracy is: {0}'.format(accuracy_score(Y_test, y_predict))
print 'error is: {0}'.format(confusion_matrix(Y_test, y_predict))
print ' ' if __name__ == '__main__':
# naive Bayes
muNB = GaussianNB() # load data
iris = load_iris()
print "np.shape(iris.data): ", np.shape(iris.data) # feature vec
X_train = iris.data[:int(len(iris.data)*0.8)]
X_test = iris.data[int(len(iris.data)*0.8):]
# label
Y_train = iris.target[:int(len(iris.data)*0.8)]
Y_test = iris.target[int(len(iris.data)*0.8):] # shuffle
X_train, Y_train = shuffle(X_train, Y_train)
X_test, Y_test = shuffle(X_test, Y_test) # expend feature
X_train = feature_expend(X_train)
X_test = feature_expend(X_test)
print "X_test: ", X_test # show Pearson Relevance
for i in range(len(X_train[])):
print "Pearson Relevance X[{0}]: ".format(i), numpy.corrcoef(np.array([i[] for i in X_train[:, i:i+]]), Y_train)[, ] model_tain_and_test(X_train, X_test, Y_train, Y_test, len(X_train[]))
我们用random函数模拟了一个无用的新特征,可以看到,无用的特征对模型不但没有帮助,反而降低了模型的性能。
至此,我们得出如下结论:
特征不是越多越多,机器学习不是洗衣机,一股脑将所有特征都丢进去,然后双手合十,指望着模型能施展魔法,自动筛选出有用的好特征,当然,dropout/正则化这些手段确实有助于提高模型性能,它们的工作本质也是通过去除一些特征,从而缓解垃圾特征对模型带来的影响。
当然,未来也许会发展出autoFeature的工程技术,但是作为数据科学工作者,我们自己必须要理解特征工程的意义。
0x4:特征加工对模型性能的影响
所谓的“特征加工”,具体来说就是对原始的特征进行线性变换(拉伸和旋转),得到新的特征,例如:
- X_i * X_j
- X_i ^ 2
- X_i / X_j
- X_i + X_j
- X_i - X_j
本质上来说,我们可以将深度神经网络的隐层看做是一种特征加工操作,稍有不同的是,深度神经网络中激活函数充当了非线性扭曲的作用,不过其本质思想还是不变的。
那接下来问题是,特征加工对模型的性能有没有影响呢?
准确的回答是,特征加工对模型的影响取决于新增特征的相关度,以及坏特征在所有特征中的占比。
我们来通过几个实验解释上面这句话,下面笔者先通过模拟出几个典型场景,最终给出总结结论:
1. 新增的特征和目标target相关度很低,同时该坏特征的占比还很高
# -*- coding: utf- -*- from sklearn.naive_bayes import GaussianNB
import numpy as np
from sklearn.datasets import load_iris
from sklearn.metrics import accuracy_score
from sklearn.metrics import confusion_matrix
import numpy
from sklearn.utils import shuffle def feature_expend(feature_vec):
# colum_1 * colum_2
feature_vec = np.hstack((feature_vec, np.array([[i] for i in np.multiply(feature_vec[:, ], feature_vec[:, ])])))
# colum_1 / colum_2
# feature_vec = np.hstack((feature_vec, np.array([[i] for i in np.divide(feature_vec[:, ], feature_vec[:, ])])))
# colum_3 * colum_4
# feature_vec = np.hstack((feature_vec, np.array([[i] for i in np.multiply(feature_vec[:, ], feature_vec[:, ])])))
# colum_4 * colum_1
# feature_vec = np.hstack((feature_vec, np.array([[i] for i in np.multiply(feature_vec[:, ], feature_vec[:, ])])))
# colum_1 ^
# feature_vec = np.hstack((feature_vec, np.array([[i] for i in np.multiply(feature_vec[:, ], feature_vec[:, ])])))
# colum_2 ^
# feature_vec = np.hstack((feature_vec, np.array([[i] for i in np.multiply(feature_vec[:, ], feature_vec[:, ])])))
# colum_3 ^
# feature_vec = np.hstack((feature_vec, np.array([[i] for i in np.multiply(feature_vec[:, ], feature_vec[:, ])])))
# colum_4 ^
# feature_vec = np.hstack((feature_vec, np.array([[i] for i in np.multiply(feature_vec[:, ], feature_vec[:, ])])))
return feature_vec def model_tain_and_test(X_train, X_test, Y_train, Y_test, feature_cn):
# load origin feature
X_train_vec = X_train[:, :feature_cn]
X_test_vec = X_test[:, :feature_cn] # train model
muNB.fit(X_train_vec, Y_train)
# predidct the test data
y_predict = muNB.predict(X_test_vec) print "feature_cn: ", feature_cn
print 'accuracy is: {0}'.format(accuracy_score(Y_test, y_predict))
print 'error is: {0}'.format(confusion_matrix(Y_test, y_predict))
print ' ' if __name__ == '__main__':
# naive Bayes
muNB = GaussianNB() # load data
iris = load_iris()
print "np.shape(iris.data): ", np.shape(iris.data) # feature vec
X_train = iris.data[:int(len(iris.data)*0.8)]
X_test = iris.data[int(len(iris.data)*0.8):]
# label
Y_train = iris.target[:int(len(iris.data)*0.8)]
Y_test = iris.target[int(len(iris.data)*0.8):] # shuffle
X_train, Y_train = shuffle(X_train, Y_train)
X_test, Y_test = shuffle(X_test, Y_test) # expend feature
X_train = feature_expend(X_train)
X_test = feature_expend(X_test)
print "X_test: ", X_test # show Pearson Relevance
for i in range(len(X_train[])):
print "Pearson Relevance X[{0}]: ".format(i), numpy.corrcoef(np.array([i[] for i in X_train[:, i:i+]]), Y_train)[, ] model_tain_and_test(X_train, X_test, Y_train, Y_test, len(X_train[])-)
model_tain_and_test(X_train, X_test, Y_train, Y_test, len(X_train[]))
上面代码中,我们新增了一个“colum_1 * colum_2”特征维度,并且打印了该特征的皮尔森相关度,相关度只有0.15,这是一个很差的特征。同时该坏特征占了总特征的1/5比例,是一个不低的比例。
因此在这种情况下,模型的检出效果受到了影响,下降了。原因之前也解释过,坏的特征因为累乘效应,影响了最终的概率值。
2. 新增的一批特征中,出现了少量的坏特征,即坏特征占比很低
# -*- coding: utf- -*- from sklearn.naive_bayes import GaussianNB
import numpy as np
from sklearn.datasets import load_iris
from sklearn.metrics import accuracy_score
from sklearn.metrics import confusion_matrix
import numpy
from sklearn.utils import shuffle def feature_expend(feature_vec):
# colum_1 * colum_2
feature_vec = np.hstack((feature_vec, np.array([[i] for i in np.multiply(feature_vec[:, ], feature_vec[:, ])])))
# colum_1 / colum_2
feature_vec = np.hstack((feature_vec, np.array([[i] for i in np.divide(feature_vec[:, ], feature_vec[:, ])])))
# colum_3 * colum_4
feature_vec = np.hstack((feature_vec, np.array([[i] for i in np.multiply(feature_vec[:, ], feature_vec[:, ])])))
# colum_4 * colum_1
feature_vec = np.hstack((feature_vec, np.array([[i] for i in np.multiply(feature_vec[:, ], feature_vec[:, ])])))
# colum_1 ^
feature_vec = np.hstack((feature_vec, np.array([[i] for i in np.multiply(feature_vec[:, ], feature_vec[:, ])])))
# colum_2 ^
feature_vec = np.hstack((feature_vec, np.array([[i] for i in np.multiply(feature_vec[:, ], feature_vec[:, ])])))
# colum_3 ^
feature_vec = np.hstack((feature_vec, np.array([[i] for i in np.multiply(feature_vec[:, ], feature_vec[:, ])])))
# colum_4 ^
feature_vec = np.hstack((feature_vec, np.array([[i] for i in np.multiply(feature_vec[:, ], feature_vec[:, ])])))
return feature_vec def model_tain_and_test(X_train, X_test, Y_train, Y_test, feature_cn):
# load origin feature
X_train_vec = X_train[:, :feature_cn]
X_test_vec = X_test[:, :feature_cn] # train model
muNB.fit(X_train_vec, Y_train)
# predidct the test data
y_predict = muNB.predict(X_test_vec) print "feature_cn: ", feature_cn
print 'accuracy is: {0}'.format(accuracy_score(Y_test, y_predict))
print 'error is: {0}'.format(confusion_matrix(Y_test, y_predict))
print ' ' if __name__ == '__main__':
# naive Bayes
muNB = GaussianNB() # load data
iris = load_iris()
print "np.shape(iris.data): ", np.shape(iris.data) # feature vec
X_train = iris.data[:int(len(iris.data)*0.8)]
X_test = iris.data[int(len(iris.data)*0.8):]
# label
Y_train = iris.target[:int(len(iris.data)*0.8)]
Y_test = iris.target[int(len(iris.data)*0.8):] # shuffle
X_train, Y_train = shuffle(X_train, Y_train)
X_test, Y_test = shuffle(X_test, Y_test) # expend feature
X_train = feature_expend(X_train)
X_test = feature_expend(X_test)
print "X_test: ", X_test # show Pearson Relevance
for i in range(len(X_train[])):
print "Pearson Relevance X[{0}]: ".format(i), numpy.corrcoef(np.array([i[] for i in X_train[:, i:i+]]), Y_train)[, ] model_tain_and_test(X_train, X_test, Y_train, Y_test, len(X_train[]))
在这个场景中,“colum_1 * colum_2”这个坏特征依然存在,但和上一个场景不同的是,除了这个坏特征之外,新增的特征都是好特征(相关度都很高)。
根据简单的乘积因子原理可以很容易理解,这个坏特征对最终概率数值的影响会被“稀释”,从而降低了对模型性能的影响。
至此,我们得出如下结论:
深度神经网络的隐层结构大规模增加了特征的数量。本质上,深度神经网络通过矩阵线性变换和非线性激活函数得到海量的特征维度的组合。我们可以想象到,其中一定有好特征(相关度高),也一定会有坏特征(相关度低)。
但是有一定我们可以确定,好特征的出现概率肯定是远远大于坏特征的,因为所有的特征都是从输入层的好特征衍生而来的(遗传进化思想)。那么当新增特征数量足够多的时候,从概率上就可以证明,好特征的影响就会远远大于坏特征,从而消解掉了坏特征对模型性能的影响。这就是为什么深度神经网络的适应度很强的原因之一。
用一句通俗的话来说就是:如果你有牛刀,杀鸡为啥不用牛刀?用牛刀杀鸡的好处在于,不管来的是鸡还是牛,都能自适应地保证能杀掉。
3. 不同机器学习模型中,过特征化的结构基础
冗余特征和过特征化现象在机器学习模型中并不罕见,在不同的模型中有不同的表现形式,例如:
- 朴素贝叶斯:累乘效应
- 多项式回归:累加效应
- 深度神经网络:累加、累乘效应
4. 一些工程实践指导原则
这里列举一些笔者在工程实践中总结出的一些指导性原则:
- 将benchmark作为基本操作,在探索复杂模型之前采用决策树、简单逻辑回归、朴素贝叶斯这样的基础模型进行基准测试。目的是获取目标问题真实难度的大致判断。
- 在项目开始的时候,尽量先选用小的模型,一般来说,模型复杂度越低,泛化性能越好。
- 将正则化作是机器学习训练中的标准配置,例如剪枝、dropout、正则参数惩罚等,目的是为了将决策权重集中到真正有价值的特征上。深度神经网络dropout的作用,一定程度上可以理解为,去除相关性较低的特征在最终决策中的权重,避免“低相关度特征累乘现象”导致的误差效应。
- 重视特征工程环节,对候选的特定进行相关性分析,优先选出相关度大于0.25的特征维度,筛选掉低相关度的无用特征。
特征真的越多越好吗?从特征工程角度看“garbage in,garbage out”的更多相关文章
- OA办公系统功能真的越全越好?
4.原文:http://www.jiusi.net/detail/472__776__4000__1.html 关键词:oa系统,OA办公系统 OA办公系统功能真的越全越好? 很多企业在选择OA办公系 ...
- CEO 系列之一:如何当好创业公司 CEO?(不要用战术的勤奋掩盖战略的懒惰,在创业过程中,最核心问题,就是能把创业情怀变成具体问题。这个问题越具体越好)
1. 创业公司要先定一个目标,要善于把目标简化, 分解成一个, 一个更具体,更简单的问题2. 针对简单的问题进行聚焦, 做深做强3. 在做的过程中, 把断地推出自己的产品到市场上去试错, 要用事实来证 ...
- Android手机越用越卡?
一直不懂Android手机为什么会越用越卡,而ios就几乎能一直保持流畅度.后来发现这个锅不该google背,而是国内混乱的Android软件开发商的锅.主要是Android对应用没什么限制,而goo ...
- 【腾讯优测干货分享】越用越卡为哪般——如何降低App的待机内存(一)
本文来自于腾讯优测公众号(wxutest),未经作者同意,请勿转载,原文地址:http://mp.weixin.qq.com/s/1_FKMbi1enpcKMqto-o_FQ 作者:腾讯TMQ专项测试 ...
- F# 越用越喜欢
F# 越用越喜欢 最近由于需要,把遗忘了几年的F#又捡了起来.说捡了起来,倒不如说是从头学习,原来学的早已经忘了!所谓学过,只不过看过一本<F# 语言程序设计> (郑宇军 凌海风 编著 - ...
- 为什么Android手机总是越用越慢?
根据第三方的调研数据显示,有77%的Android手机用户承认自己曾遭遇过手机变慢的影响,百度搜索“Android+卡慢”,也有超过460万条结果.在业内,Android手机一直有着“越用越慢”的口碑 ...
- 为啥Android手机总会越用越慢?
转自:http://www.androidchina.net/818.html 根据第三方的调研数据显示,有77%的Android手机用户承认自己曾遭遇过手机变慢的影响,百度搜索“Android+卡慢 ...
- docker-compose是个好东西,越用越香
回顾前文 前文演示了在单一容器中部署 Nginx和ASP.NET Core WebApp, 正在前文评论区某大牛指出的,容器化部署 nginx+ASP.NET Core 有更符合实战的部署选择:多容 ...
- JavaScript定时器越走越快的问题
目录 JavaScript定时器越走越快的问题 (setinterval)多次初始化 清除(clearInterval)的失效 解决方法 JavaScript定时器越走越快的问题 之前在项目中写了定时 ...
随机推荐
- elasticsearch倒排索引与TF-IDF算法
elasticsearch专栏:https://www.cnblogs.com/hello-shf/category/1550315.html 一.倒排索引(Inverted Index)简介 在关系 ...
- 品Spring:负责bean定义注册的两个“排头兵”
别看Spring现在玩的这么花,其实它的“筹码”就两个,“容器”和“bean定义”. 只有先把bean定义注册到容器里,后续的一切可能才有可能成为可能. 所以在进阶的路上如果要想走的顺畅些,彻底搞清楚 ...
- Kerberoasting攻击
前面我们介绍了<Windows本地认证>.<Windows网络认证>.<Windows域认证>和<SPN扫描>,这次继续讲解域内相关的东西. 0x01介 ...
- 用Python怎么SSH到网络设备
0. 前言 自上一篇文章<用python怎么telnet到网络设备>,简单使用了telnetlib库给大家演示了下,但是,现实环境中仍不建议去使用telnet. SSH(Secure Sh ...
- Spring MVC-从零开始-@RequestMapping 注解headers 属性
package com.jt; import org.springframework.stereotype.Controller; import org.springframework.web.bin ...
- 【ASP.NET-中级】SQLHelper数据访问公共类
ASP.NET开发中的三层开发思想指的是UI层(界面显示层),BLL层(业务逻辑层),DAL层(数据访问层)三层,三层之间通过函数的调用来达到降低耦合,易于系统维护的目的,SQLHelper助手类的主 ...
- git 工作流中的 Sourcetree 和命令行操作对比
git 工作流操作 1.初始化本地仓库文件夹 终端进入项目文件夹 git init 隐藏文件夹中有 .git 文件夹则初始化成功 2.git 查看仓库状态 这里以新建一个 demo.txt 为例 ① ...
- [WP8.1]RSA 使用BouncyCastle 公钥解密
写应用的时候遇到个服务器返回私钥加密过的数据 ,然后要在客户端用公钥解密的需求 ,一直没找到方法,应用搁置了一个学期,多方搜索,结论就是.net没有实现公钥解密的方法,要自己实现,于是硬着头皮开始看 ...
- mybatis入门百分百
今天重新返回来看自己的mybatis,总结了一些更好入门的办法,下面用最简单的方法带领大家入门. 此处先引入类包的关系图片 1.构建一个==普通==maven项目 构建好之后向pom.xml添加一下依 ...
- Flutter学习笔记(29)--Flutter如何与native进行通信
如需转载,请注明出处:Flutter学习笔记(29)--Flutter如何与native进行通信 前言:在我们开发Flutter项目的时候,难免会遇到需要调用native api或者是其他的情况,这时 ...