机器学习 | 吴恩达机器学习第七周编程作业(Python版)

匿名 (未验证) 提交于 2019-12-02 22:11:45

实验指导书

本篇博客主要讲解,吴恩达机器学习第七周的编程作业,包含两个实验,一是线性svm和带有高斯核函数的svm的基本使用;二是利用svm进行垃圾邮件分类。原始实验使用Matlab实现,本篇博客提供Python版本。

Ŀ¼

1.实验包含文件

4.垃圾邮件分类

5.垃圾邮件分类完整项目代码


1.实验包含文件

文件名称 含义
ex6.py 实验1主程序
ex6data1.mat 实验1的数据集1
ex6data2.mat 实验1的数据集2
ex6data3.mat 实验1的数据集3
plotData.py 可视化2D数据集
visualizeBoundary.py 可视化决策边界
gaussianKernel.py 高斯核函数
ex6_spam.py 实验2垃圾邮件分类主程序
spamTrain.mat 邮件训练集
spamTest.mat 邮件测试集
spamSample1.txt 垃圾邮件1例子
spamSample2.txt 垃圾邮件2例子
vocab.txt 词汇表
emailSample1.txt 邮件1例子
emailSample2.txt 邮件2例子
processEmail.py 邮件预处理
emailFeatures.py 从邮件中提取特征

完成红色部分程序的关键代码。

  • 打开实验1主程序ex6.py
 '''第1部分 加载并可视化数据集'''  print('Loading and Visualizing data ... ')   data = scio.loadmat('ex6data1.mat') #加载矩阵格式的数据集 X = data['X']   #提取原始输入特征矩阵 y = data['y'].flatten() #提取标签 并转换为1维数组 m = y.size #样本数  # 可视化训练集 pd.plot_data(X, y)
  • 编写plotData.py
 def plot_data(X, y):     plt.figure()      positive=X[y==1] #提取正样本     negtive=X[y==0]  #提取负样本          plt.scatter(positive[:,0],positive[:,1],marker='+',label='y=1') #画出正样本     plt.scatter(negtive[:,0],negtive[:,1],marker='o',label='y=0') #画出负样本     plt.legend() #显示图例
  • 数据集1可视化效果

数据集1只有两个原始输入特征,是线性可分的。

  • 训练线性SVM
  '''第2部分 训练SVM(线性核函数) 并可视化决策边界'''  print('Training Linear SVM')  #SVM的代价函数以及训练不用自己写 直接调用程序包 c = 1000 #SVM参数 可以通过改变他来观察决策边界的变化 clf = svm.SVC(c, kernel='linear', tol=1e-3)  #声明一个线性SVM clf.fit(X, y)  #训练线性SVM  pd.plot_data(X, y)  #可视化训练集 vb.visualize_boundary(clf, X, 0, 4.5, 1.5, 5) #可视化决策边界
  • 查看可视化决策边界的程序visualizeBoundary.py
 def visualize_boundary(clf, X, x_min, x_max, y_min, y_max): #x,y轴的取值范围     h = .02     xx, yy = np.meshgrid(np.arange(x_min, x_max, h), np.arange(y_min, y_max, h))#在x,y轴上以0.02为间隔,生成网格点     Z = clf.predict(np.c_[xx.ravel(), yy.ravel()])#预测每个网格点的类别0/1     Z = Z.reshape(xx.shape) #转型为网格的形状     plt.contour(xx, yy,Z, level=[0],colors='r')  #等高线图 将0/1分界线(决策边界)画出来
  • 查看决策边界

当参数C=1000比较大时,可以让我们对大间隔有一个直观的理解。但是如果训练集中存在异常点,C很大时模型会对异常点敏感,高方差(过拟合),如下所示:

这种情况下,应该把C调的小一些,此次会忽略异常点的影响,得到一个大间距的决策边界。

当C=10时:

当C=1时:

但不要把C调的太小,否则会出现高偏差(欠拟合).

  • 利用高斯核函数计算向量相似度
 '''第3部分 实现高斯核函数'''  print('Evaluating the Gaussian Kernel')  x1 = np.array([1, 2, 1]) x2 = np.array([0, 4, -1]) sigma = 2 #高斯核函数参数  sim = gk.gaussian_kernel(x1, x2, sigma) #利用高斯核函数计算两个向量的相似度  print('Gaussian kernel between x1 = [1, 2, 1], x2 = [0, 4, -1], sigma = {} : {:0.6f}\n'       '(for sigma = 2, this value should be about 0.324652'.format(sigma, sim))
  • 编写高斯核函数gaussianKernel.py

 def gaussian_kernel(x1, x2, sigma):     x1 = x1.flatten() #z转换为1维数组     x2 = x2.flatten()      sim = 0      sim=np.exp(((x1-x2).dot(x1-x2))/(-2*sigma*sigma))      return sim

与期望值进行比较,验证程序正确性:

  • 可视化训练集2
 '''第4部分 可视化数据集2'''  print('Loading and Visualizing Data ...')   data = scio.loadmat('ex6data2.mat')#加载矩阵格式的数据集2 X = data['X']  #提取原始输入特征矩阵   2个原始特征 y = data['y'].flatten() #提取标签  转换为1维数组 m = y.size  #样本数  #可视化训练集2 pd.plot_data(X, y)
  • 可视化效果

很显然训练集2是线性不可分的,而且原始输入特征维度n=2,比较小,训练样本数m比较多,此时可以考虑使用带高斯核函数的SVM。

  • 使用带有高斯核函数的SVM进行训练
 '''第5部分 对训练集2使用带有高斯核函数的SVM进行训练'''  print('Training SVM with RFB(Gaussian) Kernel (this may take 1 to 2 minutes) ...')  #参数设置 c = 1 sigma = 0.1  #调用自己写的高斯核函数  返回新的特征向量矩阵 def gaussian_kernel(x_1, x_2):     n1 = x_1.shape[0]     n2 = x_2.shape[0]     result = np.zeros((n1, n2))      for i in range(n1):         for j in range(n2):             result[i, j] = gk.gaussian_kernel(x_1[i], x_2[j], sigma)      return result  # clf = svm.SVC(c, kernel=gaussian_kernel) #使用自己手写的高斯核函数 clf = svm.SVC(c, kernel='rbf', gamma=np.power(sigma, -2)) #使用封装好的高斯核函数 rbf  clf.fit(X, y)  #进行训练  print('Training complete!')  pd.plot_data(X, y) #可视化训练集 vb.visualize_boundary(clf, X, 0, 1, .4, 1.0)  #可视化决策边界

注意当原始特征取值范围差异很大时,要先进行特征缩放,再使用核函数生成新的特征矩阵,再用SVM训练。不过上例中不需要特征缩放。

  • 可视化分类效果

  • 可视化训练集3
 '''第6部分 可视化训练集3'''  print('Loading and Visualizing Data ...')   data = scio.loadmat('ex6data3.mat')#加载矩阵格式的数据集2 X = data['X']#提取原始输入特征矩阵   2个原始特征 y = data['y'].flatten()#提取标签  转换为1维数组 m = y.size #样本数  # 可视化训练集 pd.plot_data(X, y)
  • 训练集3可视化效果

训练集3存在一些异常点。

  • 使用带有高斯核函数的SVM进行训练
 '''第7部分 对训练集3使用带有高斯核函数的SVM进行训练'''  clf = svm.SVC(c, kernel='rbf', gamma=np.power(sigma, -2)) #使用封装好的高斯核函数 rbf  clf.fit(X, y)#进行训练  pd.plot_data(X, y) #可视化训练集 vb.visualize_boundary(clf, X, -.5, .3, -.8, .6) #可视化决策边界
  • 可视化分类效果

下载链接

4.垃圾邮件分类

  • 打开主程序ex6_spam.py
 '''第1部分 邮件预处理''' #提取邮件中的单词 并得到这些单词在词汇表中的序号 存储在数组中 print('Preprocessing sample email (emailSample1.txt) ...')  file_contents = open('emailSample1.txt', 'r').read() #读入示例邮件内容 word_indices = pe.process_email(file_contents) #对邮件内容进行预处理   print('Word Indices: ') print(word_indices)
  • 编写邮件处理程序processEmail.py
 def process_email(email_contents):     vocab_list = get_vocab_list() #得到词汇字典      word_indices = np.array([], dtype=np.int64) #存储邮件中的单词在词汇标中的序号     word_list=[]     #邮件预处理         email_contents = email_contents.lower() #将邮件内容转换为小写     #去除所有html标签     email_contents = re.sub('<[^<>]+>', ' ', email_contents)      # 把邮件内容中任何数字替换为number     email_contents = re.sub('[0-9]+', 'number', email_contents)       # 把邮件中任何以http或https开头的url 替换为httpadddr     email_contents = re.sub('(http|https)://[^\s]*', 'httpaddr', email_contents)      # 把邮件中任何邮箱地址替换为 emailaddr     email_contents = re.sub('[^\s]+@[^\s]+', 'emailaddr', email_contents)      # 把邮件中的$符号 替换为dollar     email_contents = re.sub('[$]+', 'dollar', email_contents)      # 对邮件分词      print('==== Processed Email ====')      stemmer = nltk.stem.porter.PorterStemmer()      # print('email contents : {}'.format(email_contents))      tokens = re.split('[@$/#.-:&*+=\[\]?!(){\},\'\">_<;% ]', email_contents)           for token in tokens:         token = re.sub('[^a-zA-Z0-9]', '', token)         token = stemmer.stem(token)          if len(token) < 1:             continue         if token in vocab_list.values():             word_list.append(list(vocab_list.keys())[list(vocab_list.values()).index(token)])                  print(token)      print('==================')     word_indices=np.array(word_list,dtype=np.int64)      return word_indices   def get_vocab_list():  #得到词汇表     vocab_dict = {}   #以字典形式获取     with open('vocab.txt') as f:  #打开txt格式的词汇表         for line in f:             (val, key) = line.split()  #读取每一行的键和值             vocab_dict[int(val)] = key #存放到字典中      return vocab_dict 
  • 邮件中的单词在词汇表中的序号

  • 提取邮件特征
 '''第2部分 邮件特征提取'''  #将邮件内容转换为向量 基于之前提取的邮件单词在词汇表中的序号 print('Extracting Features from sample email (emailSample1.txt) ... ')   features = ef.email_features(word_indices)   print('Length of feature vector: {}'.format(features.size)) #特征向量大小 print('Number of non-zero entries: {}'.format(np.flatnonzero(features).size)) #向量中非0项的数量
  • 编写特征提取程序emailFeatures.py
 def email_features(word_indices):          n = 1899 #词汇表中词的数量  特征向量大小         features = np.zeros(n + 1) #n+1 单词序号从1开始 不+1的话 出现序号为1899的单词 就会越界          features[word_indices]=1       features=features[1:]     return features 
  • 训练线性SVM进行垃圾邮件分类
  '''第3部分 训练线性SVM进行垃圾邮件分类'''   data = scio.loadmat('spamTrain.mat') #加载矩阵格式的邮件数据集 X = data['X']  #输入特征矩阵 y = data['y'].flatten()  #标签 并转换为一维数组 0/1  print('Training Linear SVM (Spam Classification)') print('(this may take 1 to 2 minutes)')  c = 0.1 clf = svm.SVC(c, kernel='linear') clf.fit(X, y)  #训练svm  p = clf.predict(X) #在训练集上进行预测  print('Training Accuracy: {}'.format(np.mean(p == y) * 100)) #训练集上的准确率

  • 计算线性svm分类器在测试集上的准确率
 '''第4部分 在测试集上测试线性svm分类器的准确率''' # 加载测试集 data = scio.loadmat('spamTest.mat') Xtest = data['Xtest'] ytest = data['ytest'].flatten()  print('Evaluating the trained linear SVM on a test set ...')  p = clf.predict(Xtest) #在测试集上的预测结果  print('Test Accuracy: {}'.format(np.mean(p == ytest) * 100)) #测试集上的准确率

  • 返回权重最大的前15个单词及其权重
  '''第5部分 通过训练好的分类器 打印权重最高的前15个词 邮件中出现这些词更容易是垃圾邮件'''  vocab_list = pe.get_vocab_list() #得到词汇表 存在字典中 indices = np.argsort(clf.coef_).flatten()[::-1] #对权重序号进行从大到小排序 并返回 print(indices)  for i in range(15): #打印权重最大的前15个词 及其对应的权重      print('{} ({:0.6f})'.format(vocab_list[indices[i]], clf.coef_.flatten()[indices[i]]))

5.垃圾邮件分类完整项目代码

下载链接

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!