CrossValidation十字交叉验证的Python实现
1.原理
1.1 概念
交叉验证(Cross-validation)主要用于模型训练或建模应用中,如分类预测、PCR、PLS回归建模等。在给定的样本空间中,拿出大部分样本作为训练集来训练模型,剩余的小部分样本使用刚建立的模型进行预测,并求这小部分样本的预测误差或者预测精度,同时记录它们的加和平均值。这个过程迭代K次,即K折交叉。其中,把每个样本的预测误差平方加和,称为PRESS(predicted Error Sum of Squares)。
1.2 目的
用交叉验证的目的是为了得到可靠稳定的模型。在分类,建立PC 或PLS模型时,一个很重要的因素是取多少个主成分的问题。用cross validation校验每个主成分下的PRESS值,选择PRESS值小的主成分数。或PRESS值不再变小时的主成分数。
常用的精度测试方法主要是交叉验证,例如10折交叉验证(10-fold cross validation),将数据集分成十份,轮流将其中9份做训练1份做验证,10次的结果的均值作为对算法精度的估计,一般还需要进行多次10折交叉验证求均值,例如:10次10折交叉验证,以求更精确一点。
交叉验证有时也称为交叉比对,如:10折交叉比对
1.3 常见的交叉验证形式:
Holdout 验证
方法:将原始数据随机分为两组,一组做为训练集,一组做为验证集,利用训练集训练分类器,然后利用验证集验证模型,记录最后的分类准确率为此Hold-OutMethod下分类器的性能指标.。Hold-OutMethod相对于K-fold Cross Validation 又称Double cross-validation ,或相对K-CV称 2-fold cross-validation(2-CV)
一般来说,Holdout 验证并非一种交叉验证,因为数据并没有交叉使用。 随机从最初的样本中选出部分,形成交叉验证数据,而剩余的就当做训练数据。 一般来说,少于原本样本三分之一的数据被选做验证数据。
- 优点:好处的处理简单,只需随机把原始数据分为两组即可
- 缺点:严格意义来说Hold-Out Method并不能算是CV,因为这种方法没有达到交叉的思想,由于是随机的将原始数据分组,所以最后验证集分类准确率的高低与原始数据的分组有很大的关系,所以这种方法得到的结果其实并不具有说服性.(主要原因是 训练集样本数太少,通常不足以代表母体样本的分布,导致 test 阶段辨识率容易出现明显落差。此外,2-CV 中一分为二的分子集方法的变异度大,往往无法达到「实验过程必须可以被复制」的要求。)
K-fold cross-validation
K折交叉验证,初始采样分割成K个子样本,一个单独的子样本被保留作为验证模型的数据,其他K-1个样本用来训练。交叉验证重复K次,每个子样本验证一次,平均K次的结果或者使用其它结合方式,最终得到一个单一估测。这个方法的优势在于,同时重复运用随机产生的子样本进行训练和验证,每次的结果验证一次,10折交叉验证是最常用的。
- 优点:K-CV可以有效的避免过学习以及欠学习状态的发生,最后得到的结果也比较具有说服性.
- 缺点:K值选取上
留一验证
正如名称所建议, 留一验证(LOOCV)意指只使用原本样本中的一项来当做验证资料, 而剩余的则留下来当做训练资料。 这个步骤一直持续到每个样本都被当做一次验证资料。 事实上,这等同于 K-fold 交叉验证是一样的,其中K为原本样本个数。 在某些情况下是存在有效率的演算法,如使用kernel regression 和Tikhonov regularization。
2.深入
使用交叉验证方法的目的主要有3个:
- (1)从有限的学习数据中获取尽可能多的有效信息;
- (2)交叉验证从多个方向开始学习样本的,可以有效的避免陷入局部最小值;
- (3)可以在一定程度上避免过拟合问题。
采用交叉验证方法时需要将学习数据样本分为两部分:训练数据样本和验证数据样本。并且为了得到更好的学习效果,无论训练样本还是验证样本都要尽可能参与学习。一般选取10重交叉验证即可达到好的学习效果。下面在上述原则基础上设计算法,主要描述下算法步骤,如下所示。
Algorithm
Step1: 将学习样本空间 C 分为大小相等的 K 份
Step2: for i = 1 to K :取第i份作为测试集for j = 1 to K:if i != j:将第j份加到训练集中,作为训练集的一部分end ifend forend for
Step3: for i in (K-1训练集):训练第i个训练集,得到一个分类模型使用该模型在第N个数据集上测试,计算并保存模型评估指标end for
Step4: 计算模型的平均性能
Step5: 用这K个模型在最终验证集的分类准确率平均值作为此K-CV下分类器的性能指标.
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
3.实现
3.1 scikit-learn交叉验证
在scikit-learn中有CrossValidation的实现代码,地址: scikit-learn官网crossvalidation文档
使用方法:
首先加载数据集
>>> import numpy as np
>>> from sklearn import cross_validation
>>> from sklearn import datasets
>>> from sklearn import svm
>>> iris = datasets.load_iris()
>>> iris.data.shape, iris.target.shape
((150, 4), (150,))
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 1
- 2
- 3
- 4
- 5
- 6
- 7
通过上面代码,数据集特征和类标签分别为iris.data, iris.target,接着进行交叉验证
>>> X_train, X_test, y_train, y_test = cross_validation.train_test_split(
... iris.data, iris.target, test_size=0.4, random_state=0)
>>> X_train.shape, y_train.shape
((90, 4), (90,))
>>> X_test.shape, y_test.shape
((60, 4), (60,))
>>> clf = svm.SVC(kernel='linear', C=1).fit(X_train, y_train)
>>> clf.score(X_test, y_test)
0.96...
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
上面的clf是分类器,可以自己替换,比如我可以使用RandomForest
clf = RandomForestClassifier(n_estimators=400)
- 1
- 1
一个比较有用的函数是train_test_split。功能是从样本中随机的按比例选取train data和test data。形式为
X_train, X_test, y_train, y_test = cross_validation.train_test_split(train_data,train_target, test_size=0.4, random_state=0)
- 1
- 1
test_size是样本占比。如果是整数的话就是样本的数量。random_state是随机数的种子。
当然,也可以换成别的,具体算法可以参考 scikit-learn官方文档
3.2 抽样与CV结合
由于我跑的实验,数据是非均衡数据,不能直接套用,所以这里自己写了一个交叉验证的代码,仅供参考,如有问题,欢迎交流。
首先有一个自适应的数据加载函数,主要用于加载本地文本数据,同时文本每行数据以”\t”隔开,最后一列为类标号,数据样例如下:
A1001 708 K -4 -3 6 2 -13 0 2 -4 -4 -10 -9 1
A1002 709 L -4 -4 -1 -2 -11 -1 0 -12 -7 -5 -1 -1
A1003 710 G 0 -6 -2 -6 -8 -4 -6 -6 -9 -4 0 -1
A1004 711 R 0 0 1 -3 -10 -1 -3 -4 -6 -9 -6 1
- 1
- 2
- 3
- 4
- 1
- 2
- 3
- 4
说明:前面三个不是特征,所以在加载数据集的时候,特征部分起始位置修改了下,loadDataSet函数如下:
def loadDataSet(fileName):fr = open(fileName)dataMat = []; labelMat = []for eachline in fr:lineArr = []curLine = eachline.strip().split('\t') #remove '\n'for i in range(3, len(curLine)-1):lineArr.append(float(curLine[i])) #get all feature from inpurfiledataMat.append(lineArr)labelMat.append(int(curLine[-1])) #last one is class lablefr.close()return dataMat,labelMat
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
返回的dataMat为纯特征矩阵,labelMat为类别标号。
下面的splitDataSet用来切分数据集,如果是十折交叉,则split_size取10,filename为整个数据集文件,outdir则是切分的数据集的存放路径。
def splitDataSet(fileName, split_size,outdir):if not os.path.exists(outdir): #if not outdir,makrdiros.makedirs(outdir)fr = open(fileName,'r')#open fileName to readnum_line = 0onefile = fr.readlines()num_line = len(onefile) arr = np.arange(num_line) #get a seq and set len=numLinenp.random.shuffle(arr) #generate a random seq from arrlist_all = arr.tolist()each_size = (num_line+1) / split_size #size of each split setssplit_all = []; each_split = []count_num = 0; count_split = 0 #count_num 统计每次遍历的当前个数#count_split 统计切分次数for i in range(len(list_all)): #遍历整个数字序列each_split.append(onefile[int(list_all[i])].strip()) count_num += 1if count_num == each_size:count_split += 1 array_ = np.array(each_split)np.savetxt(outdir + "/split_" + str(count_split) + '.txt',\array_,fmt="%s", delimiter='\t') #输出每一份数据split_all.append(each_split) #将每一份数据加入到一个list中each_split = []count_num = 0return split_all
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
underSample(datafile)方法为抽样函数,强正负样本比例固定为1:1,返回的是一个正负样本比例均等的数据集合。
def underSample(datafile): #只针对一个数据集的下采样dataMat,labelMat = loadDataSet(datafile) #加载数据pos_num = 0; pos_indexs = []; neg_indexs = [] for i in range(len(labelMat)):#统计正负样本的下标 if labelMat[i] == 1:pos_num +=1pos_indexs.append(i)continueneg_indexs.append(i)np.random.shuffle(neg_indexs)neg_indexs = neg_indexs[0:pos_num]fr = open(datafile, 'r')onefile = fr.readlines()outfile = []for i in range(pos_num):pos_line = onefile[pos_indexs[i]] outfile.append(pos_line)neg_line= onefile[neg_indexs[i]] outfile.append(neg_line)return outfile #输出单个数据集采样结果
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
下面的generateDataset(datadir,outdir)方法是从切分的数据集中留出一份作为测试集(无需抽样),对其余的进行抽样然后合并为一个作为训练集,代码如下:
def generateDataset(datadir,outdir): #从切分的数据集中,对其中九份抽样汇成一个,\#剩余一个做为测试集,将最后的结果按照训练集和测试集输出到outdir中if not os.path.exists(outdir): #if not outdir,makrdiros.makedirs(outdir)listfile = os.listdir(datadir)train_all = []; test_all = [];cross_now = 0for eachfile1 in listfile:train_sets = []; test_sets = []; cross_now += 1 #记录当前的交叉次数for eachfile2 in listfile:if eachfile2 != eachfile1:#对其余九份欠抽样构成训练集one_sample = underSample(datadir + '/' + eachfile2)for i in range(len(one_sample)):train_sets.append(one_sample[i])#将训练集和测试集文件单独保存起来with open(outdir +"/test_"+str(cross_now)+".datasets",'w') as fw_test:with open(datadir + '/' + eachfile1, 'r') as fr_testsets:for each_testline in fr_testsets: test_sets.append(each_testline) for oneline_test in test_sets:fw_test.write(oneline_test) #输出测试集test_all.append(test_sets)#保存训练集with open(outdir+"/train_"+str(cross_now)+".datasets",'w') as fw_train:for oneline_train in train_sets: oneline_train = oneline_trainfw_train.write(oneline_train)#输出训练集train_all.append(train_sets)#保存训练集return train_all,test_all
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
因为需要评估交叉验证,所以我写了一个performance方法根据真实类标签纸和预测值来计算SN和SP,当然如果需要其他的评估标准,继续添加即可。
def performance(labelArr, predictArr):#类标签为int类型#labelArr[i] is actual value,predictArr[i] is predict valueTP = 0.; TN = 0.; FP = 0.; FN = 0. for i in range(len(labelArr)):if labelArr[i] == 1 and predictArr[i] == 1:TP += 1.if labelArr[i] == 1 and predictArr[i] == -1:FN += 1.if labelArr[i] == -1 and predictArr[i] == 1:FP += 1.if labelArr[i] == -1 and predictArr[i] == -1:TN += 1.SN = TP/(TP + FN) #Sensitivity = TP/P and P = TP + FN SP = TN/(FP + TN) #Specificity = TN/N and N = TN + FP#MCC = (TP*TN-FP*FN)/math.sqrt((TP+FP)*(TP+FN)*(TN+FP)*(TN+FN))return SN,SP
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
classifier(clf,train_X, train_y, test_X, test_y)方法是交叉验证中每次用的分类器训练过程以及测试过程,里面使用的分类器是scikit-learn自带的。该方法会将一些训练结果写入到文件中并保存到本地,同时在最后会返回ACC,SP,SN。
def classifier(clf,train_X, train_y, test_X, test_y):#X:训练特征,y:训练标号# train with randomForest print " training begin..."clf = clf.fit(train_X,train_y)print " training end."#==========================================================================# test randomForestClassifier with testsetsprint " test begin."predict_ = clf.predict(test_X) #return type is float64proba = clf.predict_proba(test_X) #return type is float64score_ = clf.score(test_X,test_y)print " test end."#==========================================================================# Modeal EvaluationACC = accuracy_score(test_y, predict_)SN,SP = performance(test_y, predict_)MCC = matthews_corrcoef(test_y, predict_)#AUC = roc_auc_score(test_labelMat, proba)#==========================================================================#save output eval_output = []eval_output.append(ACC);eval_output.append(SN) #eval_output.append(AUC)eval_output.append(SP);eval_output.append(MCC)eval_output.append(score_)eval_output = np.array(eval_output,dtype=float)np.savetxt("proba.data",proba,fmt="%f",delimiter="\t")np.savetxt("test_y.data",test_y,fmt="%f",delimiter="\t")np.savetxt("predict.data",predict_,fmt="%f",delimiter="\t") np.savetxt("eval_output.data",eval_output,fmt="%f",delimiter="\t")print "Wrote results to output.data...EOF..."return ACC,SN,SP
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
下面的mean_fun用于求列表list中数值的平均值,主要是求ACC_mean,SP_mean,SN_mean,用来评估模型好坏。
def mean_fun(onelist):count = 0for i in onelist:count += ireturn float(count/len(onelist))
- 1
- 2
- 3
- 4
- 5
- 1
- 2
- 3
- 4
- 5
交叉验证代码
def crossValidation(clf, clfname, curdir,train_all, test_all):os.chdir(curdir)#构造出纯数据型样本集cur_path = curdirACCs = [];SNs = []; SPs =[]for i in range(len(train_all)):os.chdir(cur_path)train_data = train_all[i];train_X = [];train_y = []test_data = test_all[i];test_X = [];test_y = []for eachline_train in train_data:one_train = eachline_train.split('\t') one_train_format = []for index in range(3,len(one_train)-1):one_train_format.append(float(one_train[index]))train_X.append(one_train_format)train_y.append(int(one_train[-1].strip()))for eachline_test in test_data:one_test = eachline_test.split('\t')one_test_format = []for index in range(3,len(one_test)-1):one_test_format.append(float(one_test[index]))test_X.append(one_test_format)test_y.append(int(one_test[-1].strip()))#======================================================================#classifier start hereif not os.path.exists(clfname):#使用的分类器os.mkdir(clfname)out_path = clfname + "/" + clfname + "_00" + str(i)#计算结果文件夹if not os.path.exists(out_path):os.mkdir(out_path)os.chdir(out_path)ACC, SN, SP = classifier(clf, train_X, train_y, test_X, test_y)ACCs.append(ACC);SNs.append(SN);SPs.append(SP)#======================================================================ACC_mean = mean_fun(ACCs)SN_mean = mean_fun(SNs)SP_mean = mean_fun(SPs)#==========================================================================#output experiment resultos.chdir("../")os.system("echo `date` '" + str(clf) + "' >> log.out")os.system("echo ACC_mean=" + str(ACC_mean) + " >> log.out")os.system("echo SN_mean=" + str(SN_mean) + " >> log.out")os.system("echo SP_mean=" + str(SP_mean) + " >> log.out")return ACC_mean, SN_mean, SP_mean
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
测试:
if __name__ == '__main__':os.chdir("your workhome") #你的数据存放目录datadir = "split10_1" #切分后的文件输出目录splitDataSet('datasets',10,datadir)#将数据集datasets切为十个保存到datadir目录中#==========================================================================outdir = "sample_data1" #抽样的数据集存放目录train_all,test_all = generateDataset(datadir,outdir) #抽样后返回训练集和测试集print "generateDataset end and cross validation start"#==========================================================================#分类器部分from sklearn.ensemble import RandomForestClassifierclf = RandomForestClassifier(n_estimators=500) #使用随机森林分类器来训练clfname = "RF_1" #==========================================================================curdir = "experimentdir" #工作目录#clf:分类器,clfname:分类器名称,curdir:当前路径,train_all:训练集,test_all:测试集ACC_mean, SN_mean, SP_mean = crossValidation(clf, clfname, curdir,train_all,test_all)print ACC_mean,SN_mean,SP_mean #将ACC均值,SP均值,SN均值都输出到控制台
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
上面的代码主要用于抽样后的十倍交叉验证,该怎么设置参数,还得具体分析。
总之,交叉验证在一定程度上能够避免陷入局部最小值。一般实际操作中使用的是十折交叉验证,单具体情况还得具体分析,并没有一个统一的标准固定十倍交叉的参数或者是算法的选择以及算法参数的选择。不同的数据使用不同的算法往往会的得到不同的最优分类器。So,just try it!Happy coding!
CrossValidation十字交叉验证的Python实现相关推荐
- 交叉验证python代码_交叉验证以及python代码实现
这篇文章介绍的内容是关交叉验证以及python代码实现 ,有着一定的参考价值,现在分享给大家,有需要的朋友可以参考一下 模型选择的两种方法:正则化(典型方法).交叉验证. 这里介绍交叉验证及其pyth ...
- K折交叉验证,python 简单实现。
K折交叉验证,英文名叫做K-fold cross-validation,用来测试算法准确性.是常用的测试方法.将数据集分成K份,轮流将其中K-1份作为训练数据,1份作为测试数据,进行试验. # -*- ...
- Cross-Validation(交叉验证)详解
假设我们需要从某些候选模型中选择最适合某个学习问题的模型,我们该如何选择?以多元回归模型为例:,应该如何确定k的大小,使得该模型对解决相应的分类问题最为有效?如何在偏倚(bias)和方差(varian ...
- 五折交叉验证/K折交叉验证, python代码到底怎么写
五折交叉验证: 把数据平均分成5等份,每次实验拿一份做测试,其余用做训练.实验5次求平均值.如上图,第一次实验拿第一份做测试集,其余作为训练集.第二次实验拿第二份做测试集,其余做训练集.依此类推~ 但 ...
- K-Fold交叉验证 原理+python实现
K-交叉验证是指将原始数据分成K组(一般是均分),将每个子集数据分别做一次验证集,其余的K-1组子集数据作为训练集,这样会得到K个模型,用这K个模型最终的验证集的分类准确率的平均数作为此K-CV下分类 ...
- python 交叉验证后获取模型_Pysal:如何使用Pysal回归模型进行交叉验证? - python
我正在使用pysal进行空间回归.这就是我在做什么.我首先将训练和验证集分开 import pysal as ps m_train = ps.model.spreg.GM_Lag(ytrain, xt ...
- 5折交叉验证_交叉验证:评估模型表现
注明:本文章所有代码均来自scikit-learn官方网站 在实际情况中,如果一个模型要上线,数据分析员需要反复调试模型,以防止模型仅在已知数据集的表现较好,在未知数据集上的表现较差.即要确保模型的泛 ...
- 《scikit-learn》交叉验证
当我们训练模型的时候,首先得要有数据进行训练. 我们拥有一定的数据集的时候,我们不会用所有数据去训练,因为这样我们会不知道训练出的模型的繁华能力如何,而是习惯于划分为训练数据集和测试数据集,在训练集上 ...
- 在Mnist数据上使用k折交叉验证训练,pytorch代码到底怎么写
前言 最近学到了K折交叉验证,已经迫不及待去实验一下他的效果是不是如老师讲的一样好,特此写下本文. 本文运行环境为:sklearn.pytorch .jupyter notebook k折交叉验证介绍 ...
最新文章
- linux判断是否能上网_母亲提醒女儿:判断一个男人是否值得嫁,从这两点就能轻易看出...
- PHP+JQuery实现ajax跨域
- Vivado simulation使用简介
- shaderop的定义
- JBoss Tomcat 对 JSP 的泛型支持
- Python中文编程
- java中implement_java中 implement和extends的作用和区别详细解释
- Xcode 8 的新功能一览
- Miracle密码算法开源库(二)源码分析 :mralloc.c、mrarth0.c
- 免证书发布ipa文件真机测试
- Linux系统:page fault
- 我要悄悄学习,做一个浪漫的程序员
- 【遇见CUDA】CUDA算法效率提升关键点概述
- Push to origin/master was rejected异常解决办法
- 逝者:Django贡献最多的核心开发者Malcolm Tredinnick
- 图神经网络框架DGL实现Graph Attention Network (GAT)笔记
- vue 快速入门、常用指令(1)
- 当我尝试写一个自动写小说的AI,长路漫漫的踩坑之路 ToT
- 深脑链打地基,人工智能建高楼:DBC和AI的不解之缘
- java实现一个电梯模拟器_基于Java的捎带电梯系统