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

实验指导书  下载密码:a15g

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

 

目录

1.实验包含文件

2.实验1 SVM简单使用

3.实验1 SVM简单使用 完整项目代码

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 从邮件中提取特征

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

2.实验1 SVM简单使用

  • 打开实验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) #可视化决策边界
  • 可视化分类效果

3.实验1 SVM简单使用 完整项目代码

下载链接     下载密码:3oti

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.垃圾邮件分类完整项目代码

下载链接    下载密码:q5zc

猜你喜欢

转载自blog.csdn.net/sdu_hao/article/details/84189266