基於C#的機器學習--垃圾郵件過濾

来源:https://www.cnblogs.com/wangzhenyao1994/archive/2020/01/15/12193157.html
-Advertisement-
Play Games

在這一章,我們將建立一個垃圾郵件過濾分類模型。我們將使用一個包含垃圾郵件和非垃圾郵件的原始電子郵件數據集,並使用它來訓練我們的ML模型。我們將開始遵循上一章討論的開發ML模型的步驟。這將幫助我們理解工作流程。 在本章中,我們將討論以下主題: l 定義問題 l 準備數據 l 數據分析 l 構建數據的特 ...


  在這一章,我們將建立一個垃圾郵件過濾分類模型。我們將使用一個包含垃圾郵件和非垃圾郵件的原始電子郵件數據集,並使用它來訓練我們的ML模型。我們將開始遵循上一章討論的開發ML模型的步驟。這將幫助我們理解工作流程。

       在本章中,我們將討論以下主題:

    l  定義問題

    l  準備數據

    l  數據分析

    l  構建數據的特征

    l  邏輯回歸與朴素貝葉斯的Email垃圾郵件過濾

    l  驗證分類模型

定義問題

       讓我們從定義本章要解決的問題開始。我們可能已經對垃圾郵件很熟悉了;垃圾郵件過濾是眾電子郵件服務的基本功能。垃圾郵件對用戶來說可能是惱人的,但它們除此之外,也會帶來更多的問題和風險。例如,可以設計垃圾郵件來獲取信用卡號或銀行帳戶信息,這些信息可用於信用卡欺詐或洗錢。垃圾郵件也可以用來獲取個人數據,然後可以用於身份盜竊和各種其他犯罪。垃圾郵件過濾技術是電子郵件服務避免用戶遭受此類犯罪的重要一步。然而,有正確的垃圾郵件過濾解決方案是困難的。我們想過濾掉可疑的郵件,但同時,我們又不想過濾太多,以至於非垃圾郵件進入垃圾郵件文件夾,永遠不會被用戶看到。為瞭解決這個問題,我們將讓我們的ML模型從原始電子郵件數據集中學習,並使用主題行將可疑電子郵件歸類為垃圾郵件。我們將著眼於兩個性能指標來衡量我們的成功:準確度和召回率。我們將在以下幾節中詳細討論這些指標。

       總結我們的問題定義:

需要解決的問題時什麼?我們需要一個垃圾郵件過濾解決方案,以防止我們的用戶成為欺詐活動的受害者,同時改善用戶體驗。

為什麼這是個問題?在過濾可疑郵件和不過濾太多郵件之間取得適當的平衡是很困難的,這樣垃圾郵件仍然會進入收件箱。我們將依靠ML模型來學習如何對這些可疑郵件進行統計分類。

解決這個問題的方法有哪些?我們將建立一個分類模型,根據郵件的主題行,標記潛在的垃圾郵件。我們將使用準確性和召回率來平衡被過濾的郵件數量。

成功的標準是什麼?我們想要高回覆率(實際垃圾郵件檢索的百分比占垃圾郵件的總數),而不犧牲太多的精確率(正確分類的垃圾郵件的百分比中預測為垃圾郵件)。

 

準備數據

       現在,我們已經清楚地描述和定義了將要用ML解決的問題,接下來我們需要準備數據。通常,我們需要在數據準備步驟之前採取額外的步驟來收集我們需要的數據,但是現在,我們將使用一個預先編譯並標記為公共可用的數據集。在本章中,我們將使用CSDMC2010垃圾數據集來訓練和測試我們的模型。我們將看到一個名為SPAMTrain.label的文本文件。SPAMTrain.label文件對訓練文件夾中的每封郵件都進行了編碼,0代表垃圾郵件,1代表非垃圾郵件。我們將使用此文本文件和訓練文件夾中的電子郵件數據來構建垃圾郵件分類模型。

       我們現在擁有的是一個原始數據集,其中包含許多EML文件,其中包含關於單個電子郵件的信息,以及一個包含標記信息的文本文件。為了使這個原始數據集可用來構建垃圾郵件分類模型,我們需要做以下工作:

  1. EML文件中提取主題行:為將來的任務準備數據的第一步是從各個EML文件中提取主題和正文。我們將使用一個名為EAGetMail的包來載入和提取EML文件中的信息。使用EAGetMail包,我們可以輕鬆地從EML文件中載入和提取主題和正文內容。一旦從電子郵件中提取了主題和正文,就需要將每行數據作為一行附加到Deedle數據框架中。
  2. 將提取的數據與標簽結合起來:在從各個EML文件中提取主題和正文內容之後,我們還需要做一件事。我們需要將經過編碼的標簽(垃圾郵件為0,而非垃圾郵件為1)映射到我們在前一步中創建的數據幀的每一行。如果我們打開垃圾郵件。標簽文件與任何文本編輯器,您可以看到編碼的標簽在第一列和相應的電子郵件文件名在第二列,由一個空格分隔。使用Deedle frame的ReadCsv函數,我們可以通過指定一個空格作為分隔符來輕鬆地將這個標簽數據載入到數據框架中。一旦我們將這個標記的數據載入到一個數據框架中,我們就可以簡單地將這個數據框架的第一列添加到前面步驟中使用Deedle框架的AddColumn函數創建的其他數據框架中。
  3. 將合併後的數據導出為CSV文件:現在我們已經有了一個包含電子郵件和標簽數據的數據框架,現在可以將該數據框架導出為CSV文件,以供將來使用。使用Deedle frame的SaveCsv函數,您可以輕鬆地將數據幀保存為CSV文件。

  這個準備數據步驟的代碼如下:

 1 using Deedle;
 2 using EAGetMail;
 3 using System;
 4 using System.IO;
 5 using System.Linq;
 6 
 7 namespace 準備數據
 8 {
 9     internal class Program
10     {
11         private static void Main(string[] args)
12         {
13             // 獲取所有原始的電子郵件格式的文件
14             // TODO: 更改指向數據目錄的路徑
15             string rawDataDirPath = @"D:\工作\代碼庫\AI\垃圾郵件過濾\raw-data";
16             string[] emailFiles = Directory.GetFiles(rawDataDirPath, "*.eml");
17 
18             // 從電子郵件文件中解析出主題和正文
19             var emailDF = ParseEmails(emailFiles);
20             // 獲取每個電子郵件的標簽(spam vs. ham)
21             var labelDF = Frame.ReadCsv(rawDataDirPath + "\\SPAMTrain.label", hasHeaders: false, separators: " ", schema: "int,string");
22             // 將這些標簽添加到電子郵件數據框架中
23             emailDF.AddColumn("is_ham", labelDF.GetColumnAt<String>(0));
24             // 將解析後的電子郵件和標簽保存為CSV文件
25             emailDF.SaveCsv("transformed.csv");
26 
27             Console.WriteLine("準備數據步驟完成!");
28             Console.ReadKey();
29         }
30 
31         private static Frame<int, string> ParseEmails(string[] files)
32         {
33             // 我們將解析每個電子郵件的主題和正文,並將每個記錄存儲到鍵值對中
34             var rows = files.AsEnumerable().Select((x, i) =>
35             {
36                 // 將每個電子郵件文件載入到郵件對象中
37                 Mail email = new Mail("TryIt");
38                 email.Load(x, false);
39 
40                 // 提取主題和正文
41                 string EATrialVersionRemark = "(Trial Version)"; // EAGetMail在試用版本中附加主題“(試用版本)”
42                 string emailSubject = email.Subject.EndsWith(EATrialVersionRemark) ?
43                     email.Subject.Substring(0, email.Subject.Length - EATrialVersionRemark.Length) : email.Subject;
44                 string textBody = email.TextBody;
45 
46                 // 使用電子郵件id (emailNum)、主題和正文創建鍵-值對
47                 return new { emailNum = i, subject = emailSubject, body = textBody };
48             });
49 
50             // 根據上面創建的行創建一個數據幀
51             return Frame.FromRecords(rows);
52         }
53     }
54 }
View Code

 運行這段代碼後,程式將會創建一個名為transformed.csv的文件,它將包含四列(emailNum、subject、body和is_ham)。我們將使用此輸出數據作為後面步驟的輸入,以構建垃圾郵件過濾項目的ML模型。但是,我們也可以嘗試使用Deedle框架和EAGetMail包,以不同的方式調整和準備這些數據。我在這裡提供的代碼是準備這些原始電子郵件數據以供將來使用的一種方法,以及我們可以從原始電子郵件數據中提取的一些信息。使用EAGetMail包,我們也可以提取其他特征,比如發件人的電子郵件地址和電子郵件中的附件,這些額外的特征可能有助於改進垃圾郵件分類模型。

數據分析

       在準備數據步驟中,我們將原始數據集轉換為更具可讀性和可用性的數據集。我們現在有一個文件可以查看,以找出哪些郵件是垃圾郵件,哪些不是。此外,我們可以很容易地找到垃圾郵件和非垃圾郵件的主題行。有了這些轉換後的數據,讓我們開始看看數據實際上是什麼樣子的,看看我們能否在數據中找到任何模式或問題。

       因為我們正在處理文本數據,所以我們首先要看的是垃圾郵件和非垃圾郵件的單詞分佈有什麼不同。為此,我們需要將上一步的數據輸出轉換為單詞出現次數的矩陣表示。讓我們以數據中的前三個主題行為例,一步步地完成這一工作。我們的前三個主題如下:

 

  如果我們轉換這些數據,使每一列對應於每一個主題行中的每個單詞,並將每個單元格的值編碼為1,如果給定的主題行有單詞,則編碼為0,如果沒有,則生成的矩陣如下所示:

 

 

 

  這種特定的編碼方式稱為one-hot編碼,我們只關心特定的單詞是否出現在主題行中,而不關心每個單詞在主題行中實際出現的次數。在前面的例子中,我們還去掉了所有的標點符號,比如冒號、問號和感嘆號。要以編程方式做到這一點,我們可以使用regex將每個主題行拆分為只包含字母-數字字元的單詞,然後用one-hot編碼構建一個數據框架。完成這個編碼步驟的代碼如下:  

 1 private static Frame<int, string> CreateWordVec(Series<int, string> rows)
 2         {
 3             var wordsByRows = rows.GetAllValues().Select((x, i) =>
 4             {
 5                 var sb = new SeriesBuilder<string, int>();
 6 
 7                 ISet<string> words = new HashSet<string>(
 8                     Regex.Matches(
 9                         // 隻字母字元
10                         x.Value, "[a-zA-Z]+('(s|d|t|ve|m))?"
11                     ).Cast<Match>().Select(
12                         // 然後,將每個單詞轉換為小寫字母
13                         y => y.Value.ToLower()
14                     ).ToArray()
15                 );
16 
17                 // 對每行出現的單詞進行1的編碼
18                 foreach (string w in words)
19                 {
20                     sb.Add(w, 1);
21                 }
22 
23                 return KeyValue.Create(i, sb.Series);
24             });
25 
26             // 從我們剛剛創建的行創建一個數據框架 並將缺失的值編碼為0
27             var wordVecDF = Frame.FromRows(wordsByRows).FillMissing(0);
28 
29             return wordVecDF;
30         }
View Code

  有了這種one-hot編碼矩陣表示的單詞,使我們的數據分析過程變的更容易。例如,如果我們想查看垃圾郵件中出現頻率最高的10個單詞,我們可以簡單地對垃圾郵件的一個one-hot編碼單詞矩陣的每一列的值進行求和,然後取求和值最高的10個單詞。這正是我們在以下代碼中所做的:

 1 var hamTermFrequencies = subjectWordVecDF.Where(
 2                 x => x.Value.GetAs<int>("is_ham") == 1
 3             ).Sum().Sort().Reversed.Where(x => x.Key != "is_ham");
 4 
 5             var spamTermFrequencies = subjectWordVecDF.Where(
 6                 x => x.Value.GetAs<int>("is_ham") == 0
 7             ).Sum().Sort().Reversed;
 8 
 9             // 查看排名前十的垃圾郵件和非垃圾郵件
10             var topN = 10;
11 
12             var hamTermProportions = hamTermFrequencies / hamEmailCount;
13             var topHamTerms = hamTermProportions.Keys.Take(topN);
14             var topHamTermsProportions = hamTermProportions.Values.Take(topN);
15 
16             System.IO.File.WriteAllLines(
17                 dataDirPath + "\\ham-frequencies.csv",
18                 hamTermFrequencies.Keys.Zip(
19                     hamTermFrequencies.Values, (a, b) => string.Format("{0},{1}", a, b)
20                 )
21             );
22 
23             var spamTermProportions = spamTermFrequencies / spamEmailCount;
24             var topSpamTerms = spamTermProportions.Keys.Take(topN);
25             var topSpamTermsProportions = spamTermProportions.Values.Take(topN);
26 
27             System.IO.File.WriteAllLines(
28                 dataDirPath + "\\spam-frequencies.csv",
29                 spamTermFrequencies.Keys.Zip(
30                     spamTermFrequencies.Values, (a, b) => string.Format("{0},{1}", a, b)
31                 )
32             );
View Code

  從這段代碼可以看出,我們使用Deedle的數據框架的求和方法來對每一列中的值求和,並按相反的順序排序。我們對垃圾郵件這樣做一次,對非垃圾郵件這樣做一次。然後,我們使用Take方法獲得垃圾郵件和非垃圾郵件中出現頻率最高的十個單詞。當問運行這段代碼時,它將生成兩個CSV文件:ham-frequency-cies.csv和spam-frequency-cies.csv。這兩個文件包含關於垃圾郵件和非垃圾郵件中出現的單詞數量的信息,我們將在稍後的構造數據特征和模型構建步驟中使用這些信息。

       現在讓我們將一些數據可視化,以便進一步分析。首先,看一下數據集中ham電子郵件中出現頻率最高的10個術語:

 

 

 

  從這個柱狀圖中可以看出,數據集中的非垃圾郵件比垃圾郵件要多,就像在現實世界中一樣。我們的收件箱里收到的非垃圾郵件比垃圾郵件要多。

       我們使用以下代碼來生成這個柱狀圖,以可視化數據集中的ham和spam電子郵件的分佈:

1 var barChart = DataBarBox.Show(
2                 new string[] { "Ham", "Spam" },
3                 new double[] {
4                     hamEmailCount,
5                     spamEmailCount
6                 }
7             );
8 barChart.SetTitle("Ham vs. Spam in Sample Set");
View Code

  使用Accord.Net中的DataBarBox類。我們可以很容易地在柱狀圖中可視化數據。現在讓我們來看看在ham和spam郵件中出現頻率最高的十個詞。可以使用下麵的代碼來為ham和spam郵件中排名前十的術語生成柱狀圖:

 1 var hamBarChart = DataBarBox.Show(
 2                 topHamTerms.ToArray(),
 3                 new double[][] {
 4                     topHamTermsProportions.ToArray(),
 5                     spamTermProportions.GetItems(topHamTerms).Values.ToArray()
 6                 }
 7             );
 8             hamBarChart.SetTitle("Top 10 Terms in Ham Emails (blue: HAM, red: SPAM)");
 9             System.Threading.Thread.Sleep(3000);
10             hamBarChart.Invoke(
11                 new Action(() =>
12                 {
13                     hamBarChart.Size = new System.Drawing.Size(5000, 1500);
14                 })
15             );
16 
17             var spamBarChart = DataBarBox.Show(
18                 topSpamTerms.ToArray(),
19                 new double[][] {
20                     hamTermProportions.GetItems(topSpamTerms).Values.ToArray(),
21                     topSpamTermsProportions.ToArray()
22                 }
23             );
24             spamBarChart.SetTitle("Top 10 Terms in Spam Emails (blue: HAM, red: SPAM)");
View Code

  類似地,我們使用DataBarBox類來顯示條形圖。當運行這段代碼時,我們將看到下麵的圖,其中顯示了在ham電子郵件中出現頻率最高的10個術語:

 

 

   spam郵件中最常出現的十大術語的柱狀圖如下:

 

 

 

  正如所料,垃圾郵件中的單詞分佈與非垃圾郵件有很大的不同。例如,如果你看一下上上邊的圖表,spam和hibody這兩個詞在垃圾郵件中出現的頻率很高,但在非垃圾郵件中出現的頻率不高。然而,有些事情並沒有多大意義。如果你仔細觀察,你會發現所有的垃圾郵件和非垃圾郵件都有trial和version這兩個單詞,是不太可能的。如果你在文本編輯器中打開一些原始的EML文件,你會很容易發現並不是所有的電子郵件的標題行都包含這兩個詞。

  那麼,到底發生了什麼?我們的數據是否被之前的數據準備或數據分析步驟污染了?

  進一步的研究表明,我們使用的其中一個軟體包導致了這個問題。我們用來載入和提取電子郵件內容的EAGetMail包在使用其試用版本時,會自動將(Trial Version)附加到主題行末尾。現在我們知道了這個數據問題的根本原因,我們需要回去修複它。一種解決方案是返回到數據準備步驟,用以下代碼更新ParseEmails函數,它只是從主題行刪除附加的(Trial Version)標誌:

 1 private static Frame<int, string> ParseEmails(string[] files)
 2         {
 3             // 我們將解析每個電子郵件的主題和正文,並將每個記錄存儲到鍵值對中
 4             var rows = files.AsEnumerable().Select((x, i) =>
 5             {
 6                 // 將每個電子郵件文件載入到郵件對象中
 7                 Mail email = new Mail("TryIt");
 8                 email.Load(x, false);
 9 
10                 // 提取主題和正文
11                 string EATrialVersionRemark = "(Trial Version)"; // EAGetMail在試用版本中附加主題“(試用版本)”
12                 string emailSubject = email.Subject.EndsWith(EATrialVersionRemark) ?
13                     email.Subject.Substring(0, email.Subject.Length - EATrialVersionRemark.Length) : email.Subject;
14                 string textBody = email.TextBody;
15 
16                 // 使用電子郵件id (emailNum)、主題和正文創建鍵-值對
17                 return new { emailNum = i, subject = emailSubject, body = textBody };
18             });
19 
20             // 根據上面創建的行創建一個數據幀
21             return Frame.FromRecords(rows);
22         }
View Code

  在更新了這段代碼並再次運行之前的數據準備和分析代碼之後,word分佈的柱狀圖就更有意義了。

  下麵的條形圖顯示了修複和刪除(Trial Version)標記後,ham郵件中出現頻率最高的10個術語:

 

  下麵的條形圖顯示了修複和刪除(Trial Version)標誌後spam郵件中出現頻率最高的10個術語

 

 

  這是一個很好的例子,說明瞭在構建ML模型時數據分析步驟的重要性。在數據準備和數據分析步驟之間進行迭代是非常常見的,因為我們通常會在分析步驟中發現數據的問題,通常我們可以通過更新數據準備步驟中使用的一些代碼來提高數據質量。現在,我們已經有了主題行中使用的單詞的矩陣表示形式的清晰數據,是時候開始研究我們將用於構建ML模型的實際特性了。

構建數據的特征

       在前面的步驟中,我們簡要地查看了垃圾郵件和非垃圾郵件的單詞分類,我們註意到了一些事情。首先,大量的最頻繁出現的單詞是經常使用的單詞,沒有什麼意義。例如,像to、the、For和a這樣的單詞是常用的單詞,而我們的ML演算法不會從這些單詞中學到什麼。這些類型的單詞被稱為停止單詞,它們經常被忽略或從功能集中刪除。我們將使用NLTK的停止單詞列表從功能集中過濾出常用的單詞。

       過濾這些停止字的一種方法是如下代碼所示:

1 //讀停詞表 
2 ISet<string> stopWords = new HashSet<string>(File.ReadLines(<path-to-your-stopwords.txt>);
3 //從詞頻序列中過濾出停止詞
4 var spamTermFrequenciesAfterStopWords = spamTermFrequencies.Where(
5                 x => !stopWords.Contains(x.Key)
6 );
View Code

  經過濾後,非垃圾郵件常出現的十大新詞語如下:

  過濾掉停止詞後,垃圾郵件最常出現的十大詞語如下:

  從這些柱狀圖中可以看出,過濾掉特性集中的停止詞,使得更有意義的詞出現在頻繁出現的單詞列表的頂部。然而,我們還註意到一件事。數字似乎是最常出現的單詞之一。例如,數字3和2進入了非垃圾郵件中出現頻率最高的10個單詞。數字80和70進入了垃圾郵件中出現頻率最高的10個單詞。然而,很難確定這些數字是否有助於訓練ML模型將電子郵件歸類為垃圾郵件或垃圾郵件。

       有多種方法可以從特性集中過濾掉這些數字,但是我們將只在這裡展示一種方法。我們更新了上一步中使用的正則表達式,以匹配只包含字母字元而不包含字母數字字元的單詞。下麵的代碼展示了我們如何更新CreateWordVec函數來過濾掉特性集中的數字。

 1 private static Frame<int, string> CreateWordVec(Series<int, string> rows)
 2         {
 3             var wordsByRows = rows.GetAllValues()
 4                 .Select((x, i) =>
 5                 {
 6                     var sb = new SeriesBuilder<string, int>();
 7                     ISet<string> words = new HashSet<string>(
 8                         //僅字母字元
 9                         Regex.Matches(x.Value, "[a-zA-Z]+('(s|d|t|ve|m))?")
10                         .Cast<Match>()
11                         //然後,將每個單詞轉換為小寫字母
12                         .Select(y => y.Value.ToLower())
13                         .ToArray()
14                     );
15                     //對每行出現的單詞進行1的編碼
16                     foreach (string w in words)
17                     {
18                         sb.Add(w, 1);
19                     }
20                     return KeyValue.Create(i, sb.Series);
21                 });
22             //從我們剛剛創建的行中創建一個數據幀,並用0對缺失的值進行編碼
23             var wordVecDF = Frame.FromRows(wordsByRows).FillMissing(0);
24             return wordVecDF;
25         }
View Code

  一旦我們從功能集過濾掉這些數字,非垃圾郵件的單詞分佈如下:

 

 

 而垃圾郵件的單詞分佈,在過濾掉來自功能集的數字後,看起來像這樣:

  可以從這些柱狀圖中看到,我們有更多的有意義的詞在頂部的名單上,這似乎和之前有一個很大的區別,在垃圾郵件和非垃圾郵件的單詞分佈。那些經常出現在垃圾郵件中的單詞在非垃圾郵件中似乎並不多見,反之亦然。

  一旦您運行這段代碼時,它將生成柱狀圖顯示垃圾郵件單詞分佈和非垃圾郵件和兩個單詞列表的CSV files-one非垃圾郵件與相應項出現和另一個電子郵件在垃圾郵件單詞列表和相應的項出現。在下麵的模型構建部分中,當我們為垃圾郵件過濾構建分類模型時,我們將使用這個術語頻率輸出來進行特征選擇過程。

邏輯回歸與朴素貝葉斯的Email垃圾郵件過濾

  我們已經走了很長的路,最終在c#中構建了我們的第一個ML模型。在本節中,我們將訓練邏輯回歸和朴素貝葉斯分類器來將電子郵件分為垃圾郵件和非垃圾郵件。我們將使用這兩種學習演算法來進行交叉驗證,以更好地瞭解我們的分類模型在實踐中的表現。如前一章所簡要討論的,在k-fold交叉驗證中,訓練集被劃分為k個大小相等的子集,其中一個子集作為驗證集,其餘的k-1子集用於訓練模型。然後重覆這個過程k次,在每次迭代中使用不同的子集或摺疊作為測試的驗證集,然後對相應的k驗證結果求平均值以報告單個估計。
  讓我們首先看看如何使用Accord在c#中用邏輯回歸來實例化交叉驗證演算法。代碼如下:

 1 var cvLogisticRegressionClassifier = CrossValidation.Create<LogisticRegression, 
 2                 IterativeReweightedLeastSquares<LogisticRegression>, double[], int>(
 3                     // 摺疊數量    
 4                     k: numFolds,
 5                     // 學習演算法    
 6                     learner: (p) => new IterativeReweightedLeastSquares<LogisticRegression>()
 7                     {
 8                         MaxIterations = 100,
 9                         Regularization = 1e-6
10                     },
11                     // 使用0 - 1損失函數作為成本函數 
12                     loss: (actual, expected, p) => new ZeroOneLoss(expected).Loss(actual),
13                     // 合適的分類器    
14                     fit: (teacher, x, y, w) => teacher.Learn(x, y, w),
15                     // 輸入    
16                     x: input,
17                     // 輸出    
18                     y: output
19                 );
20 // 運行交叉驗證
21 var result = cvLogisticRegressionClassifier.Learn(input, output);
View Code

  讓我們更深入地看看這段代碼。通過提供要訓練的模型類型、適合模型的學習演算法類型、輸入數據類型和輸出數據類型,我們可以使用靜態create函數創建一個新的交叉驗證演算法。對於這個例子,我們創建了一個新的交叉驗證演算法,以邏輯回歸為模型,以IterativeReweightedLeastSquares作為學習演算法,以雙數組作為輸入類型,以整數作為輸出類型(每個標簽)。您可以嘗試使用不同的學習演算法來訓練邏輯回歸模型。在協議。您可以選擇使用隨機梯度下降演算法(LogisticGradientDescent)作為適合邏輯回歸模型的學習演算法。
  對於參數,我們可以為k-fold交叉驗證(k)、帶有自定義參數的學習方法(learner)、選擇的損失/成本函數(loss)和一個知道如何使用學習演算法(fit)來擬合模型的函數(x)、輸入(x)和輸出(y)指定摺疊數。為了在本節中進行說明,我們為k-fold交叉驗證設置了一個相對較小的數字3。此外,對於最大的迭代,我們選擇了一個相對較小的數字,100,而對於迭代加權最小二乘學習演算法的正則化,我們選擇了一個相對較大的數字,le-6或1/1,000,000。對於損耗函數,我們使用一個簡單的0 - 1損耗函數,它為正確的預測分配0,為錯誤的預測分配1。這就是我們的學習演算法試圖最小化的代價函數。所有這些參數都可以進行不同的調優。我們可以選擇一個不同的損耗/成本函數,k摺疊交叉驗證中使用的摺疊數,以及學習演算法的最大迭代次數和正則化次數。我們甚至可以使用不同的學習演算法來適應邏輯回歸模型,比如LogisticGradientDescent,它將迭代地嘗試找到損失函數的局部最小值。
  我們可以用同樣的方法訓練朴素貝葉斯分類器,用k次交叉驗證。使用朴素貝葉斯學習演算法進行k-fold交叉驗證的代碼如下:

 1 var cvNaiveBayesClassifier = CrossValidation.Create<NaiveBayes<BernoulliDistribution>,
 2                 NaiveBayesLearning<BernoulliDistribution>, double[], int>(
 3                     // 摺疊的數量
 4                     k: numFolds,
 5                     // 二項分佈的朴素貝葉斯分類器
 6                     learner: (p) => new NaiveBayesLearning<BernoulliDistribution>(),
 7                     // 使用0 - 1損失函數作為成本函數
 8                     loss: (actual, expected, p) => new ZeroOneLoss(expected).Loss(actual),
 9                     // 合適的分類器
10                     fit: (teacher, x, y, w) => teacher.Learn(x, y, w),
11                     // 輸入
12                     x: input,
13                     // 輸出
14                     y: output
15                 );
16 // 運行交叉驗證
17 var result = cvNaiveBayesClassifier.Learn(input, output);
View Code

  之前的邏輯回歸模型代碼與這段代碼的唯一區別是我們選擇的模型和學習演算法。我們使用NaiveBayes作為模型,NaiveBayesLearning作為學習演算法來訓練我們的NaiveBayes分類器,而不是使用LogisticRegression和IterativeReweightedLeastSquares。由於所有的輸入值都是二進位的(0或1),所以我們使用BernoulliDistribution作為我們的朴素Byes分類器模型。
  當你運行這段代碼,你應該看到一個輸出如下:

 

 

   在下麵討論模型驗證方法的小節中,我們將進一步研究這些數字所代表的內容。為了嘗試不同的ML模型。可以使用我們前面討論過的邏輯回歸模型代碼來替換它們,或者也可以嘗試選擇不同的學習演算法使用。

驗證分類模型

  我們使用Accord.Net Framework在c#中建立了第一個ML模型。然而,我們還沒有完全完成。如果我們更仔細地查看以前的控制台輸出,就會發現有一件事非常令人擔憂的情形。訓練誤差約為0.03,而驗證誤差約為0.26。這意味著我們的分類模型在訓練集中正確預測了100次中的87次,而在驗證或測試集中正確預測了100次中的74次。這是一個典型的過度擬合的例子,其中模型與訓練集非常接近,以至於它對未預見數據集的預測是不

您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 一、泛型 泛型就是封裝,將重覆的工作簡單化 1.泛型方法 public static void Show<T>(T tParameter) { Console.WriteLine("This is {0}, parameter = {1}, type = {2}", typeof(CommonMet ...
  • 驗證碼(Captcha)基於十道安全柵欄, 為網頁、App、小程式開發者打造立體、全面的人機驗證,最大程度地保護註冊登錄、活動秒殺、點贊發帖、數據保護等各大場景下的業務安全。要做自動化腳本程式,就要能識別驗證碼這一關。 效果如下圖: ​ 演示程式結構在vs2019創建WinForm窗體程式,添加相應 ...
  • 本示例以植物大戰僵屍為例, 實現功能為 每1秒讓陽光刷新為 9999.本示例使用的游戲版本為 [植物大戰僵屍2010年度版], 使用的輔助查看記憶體地址的工具是 CE. 由於每次啟動游戲, 游戲中陽光地址都是變的, 唯一不變的基址1, 我們要通過CE工具找到基址1的地址, 可以算出陽光的地址. 基址2 ...
  • using System; using System.Windows.Forms; using System.Runtime.InteropServices; using System.Diagnostics; namespace ConsoleApplication3 { class Monito ...
  • /// <summary> /// 截取屏幕 /// </summary> /// <param name="x">起點X坐標</param> /// <param name="y">起點Y坐標</param> /// <param name="width">截取寬度</param> /// <pa ...
  • QingTing.Fm 是調用蜻蜓FM API 查詢界面內容,進行線上播放。 Release地址下載 環境 Visual Studio 2019,dotNet Framework 4.6.1 SDK 支持Windows Win7、8、10 源碼地址 效果 ...
  • // 引入必要的命名空間 using System.IO; using System.Drawing.Imaging; // 代碼部分 // private byte[] photo;//公用緩衝區 public string SourFilePath;//源圖片文件路徑 public string ...
  • using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace ConsoleApp1 { class Prog ...
一周排行
    -Advertisement-
    Play Games
  • 前言 在我們開發過程中基本上不可或缺的用到一些敏感機密數據,比如SQL伺服器的連接串或者是OAuth2的Secret等,這些敏感數據在代碼中是不太安全的,我們不應該在源代碼中存儲密碼和其他的敏感數據,一種推薦的方式是通過Asp.Net Core的機密管理器。 機密管理器 在 ASP.NET Core ...
  • 新改進提供的Taurus Rpc 功能,可以簡化微服務間的調用,同時可以不用再手動輸出模塊名稱,或調用路徑,包括負載均衡,這一切,由框架實現並提供了。新的Taurus Rpc 功能,將使得服務間的調用,更加輕鬆、簡約、高效。 ...
  • 順序棧的介面程式 目錄順序棧的介面程式頭文件創建順序棧入棧出棧利用棧將10進位轉16進位數驗證 頭文件 #include <stdio.h> #include <stdbool.h> #include <stdlib.h> 創建順序棧 // 指的是順序棧中的元素的數據類型,用戶可以根據需要進行修改 ...
  • 前言 整理這個官方翻譯的系列,原因是網上大部分的 tomcat 版本比較舊,此版本為 v11 最新的版本。 開源項目 從零手寫實現 tomcat minicat 別稱【嗅虎】心有猛虎,輕嗅薔薇。 系列文章 web server apache tomcat11-01-官方文檔入門介紹 web serv ...
  • C總結與剖析:關鍵字篇 -- <<C語言深度解剖>> 目錄C總結與剖析:關鍵字篇 -- <<C語言深度解剖>>程式的本質:二進位文件變數1.變數:記憶體上的某個位置開闢的空間2.變數的初始化3.為什麼要有變數4.局部變數與全局變數5.變數的大小由類型決定6.任何一個變數,記憶體賦值都是從低地址開始往高地 ...
  • 如果讓你來做一個有狀態流式應用的故障恢復,你會如何來做呢? 單機和多機會遇到什麼不同的問題? Flink Checkpoint 是做什麼用的?原理是什麼? ...
  • C++ 多級繼承 多級繼承是一種面向對象編程(OOP)特性,允許一個類從多個基類繼承屬性和方法。它使代碼更易於組織和維護,並促進代碼重用。 多級繼承的語法 在 C++ 中,使用 : 符號來指定繼承關係。多級繼承的語法如下: class DerivedClass : public BaseClass1 ...
  • 前言 什麼是SpringCloud? Spring Cloud 是一系列框架的有序集合,它利用 Spring Boot 的開發便利性簡化了分散式系統的開發,比如服務註冊、服務發現、網關、路由、鏈路追蹤等。Spring Cloud 並不是重覆造輪子,而是將市面上開發得比較好的模塊集成進去,進行封裝,從 ...
  • class_template 類模板和函數模板的定義和使用類似,我們已經進行了介紹。有時,有兩個或多個類,其功能是相同的,僅僅是數據類型不同。類模板用於實現類所需數據的類型參數化 template<class NameType, class AgeType> class Person { publi ...
  • 目錄system v IPC簡介共用記憶體需要用到的函數介面shmget函數--獲取對象IDshmat函數--獲得映射空間shmctl函數--釋放資源共用記憶體實現思路註意 system v IPC簡介 消息隊列、共用記憶體和信號量統稱為system v IPC(進程間通信機制),V是羅馬數字5,是UNI ...