brunch

You can make anything
by writing

C.S.Lewis

by 라인하트 Feb 03. 2021

머신러닝 옥타브 실습(6-4): 스팸 이메일 분류기

  온라인 강의 플랫폼 코세라의 창립자인 앤드류 응 (Andrew Ng) 교수는 인공지능 업계의 거장입니다. 그가 스탠퍼드 대학에서 머신 러닝 입문자에게 한 강의를 그대로 코세라 온라인 강의 (Coursera.org)에서 무료로 배울 수 있습니다. 이 강의는 머신러닝 입문자들의 필수코스입니다. 인공지능과 머신러닝을 혼자 공부하면서 자연스럽게 만나게 되는 강의입니다. 


Programming Exercise 6: 

Support Vector Machine (서포트 벡터 머신) 


2. Spam Classification (스팸 분류기)  


   Many email services today provide spam filters that are able to classify emails into spam and non-spam email with high accuracy. In this part of the exer- cise, you will use SVMs to build your own spam filter.

   You will be training a classifier to classify whether a given email, x, is spam (y = 1) or non-spam (y = 0). In particular, you need to convert each email into a feature vector x ∈ Rn. The following parts of the exercise will walk you through how such a feature vector can be constructed from an email.

   Throughout the rest of this exercise, you will be using the the script ex6 spam.m. The dataset included for this exercise is based on a a subset of the SpamAssassin Public Corpus.3 For the purpose of this exercise, you will only be using the body of the email (excluding the email headers).


   오늘날 대부분의 이메일 서비스는 스팸 이메일과 비스팸 이메일을 분류하는 스팸 필터를 제공합니다. 정확도도 상당히 높습니다. 이번 실습은 SVM을 사용하여 스팸 필터를 구현합니다. 

   분류기는 이메일 x가 스팸 (y=1)인지 비스팸(y=0)인지 분류기 하기 위해 데이터 셋을 학습합니다. 각 이메일의 피처 벡터 x ∈ R^(n)차원 벡터로 변환합니다. 다음 실습은 이메일에서 피처 벡터를 구성하는 방법을 설명합니다. 

   이번 실습은 ex6_spam.m 파일을 사용합니다. 데이터 셋은 SpamAssassin Public Corpus의 하위 집합을 사용합니다. 실습은 이메일 헤더를 제외한 본문만을 사용합니다. 



2.1 Preprocessing Emails (이메일 전처리)


   Before starting on a machine learning task, it is usually insightful to take a look at examples from the dataset. Figure 8 shows a sample email that contains a URL, an email address (at the end), numbers, and dollar amounts. While many emails would contain similar types of entities (e.g., numbers, other URLs, or other email addresses), the specific entities (e.g., the specific URL or specific dollar amount) will be different in almost every email. Therefore, one method often employed in processing emails is to “normalize” these values, so that all URLs are treated the same, all numbers are treated the same, etc. For example, we could replace each URL in the email with the unique string “httpaddr” to indicate that a URL was present.

   This has the effect of letting the spam classifier make a classification decision based on whether any URL was present, rather than whether a specific URL was present. This typically improves the performance of a spam classifier, since spammers often randomize the URLs, and thus the odds of seeing any particular URL again in a new piece of spam is very small.


   머신 러닝 학습을 시작하기 전에 데이터 셋의 예제를 살펴보는 것이 좋습니다. 그림 8은 URL, 이메일 주소, 숫자, 달러 금액을 포함한 샘플 이메일입니다. 많은 이메일에  이메일 주소, 숫자, URL이 포함되어 있지만, 특정 URL과 특정 금액은 다릅니다. 이메일 처리에 자주 사용되는 방법은 정규화를 통해 모든 URL과 모든 숫자를 동일하게 처리합니다. 예를 들어, 이메일의 각 URL을 고유한 문자열로 바꿉니다. 이메일에 URL의 유무를 나타내기 위해 'httpaddr'를 씁니다. httpaddr 은 특정 URL이 존재하는지 여부가 아닌 URL 존재 여부에 따라 분류 결정을 내리도록 하는 효과가 있고, 일반적으로 스팸 분류기의 성능을 향상합니다. 스패머는 종종 URL을 무작위로 지정하므로 새로운 스팸에서 특정 URL을 다시 볼 확률이 매우 낮습니다. 


   In processEmail.m, we have implemented the following email prepro- cessing and normalization steps:


Lower-casing: The entire email is converted into lower case, so that captialization is ignored (e.g., IndIcaTE is treated the same as Indicate).


Stripping HTML: All HTML tags are removed from the emails. Many emails often come with HTML formatting; we remove all the HTML tags, so that only the content remains.


Normalizing URLs: All URLs are replaced with the text “httpaddr”.


Normalizing Email Addresses: All email addresses are replaced          with the text “emailaddr”.


Normalizing Numbers: All numbers are replaced with the text          “number”.


Normalizing Dollars: All dollar signs ($) are replaced with the text          “dollar”.


Word Stemming: Words are reduced to their stemmed form. For example, “discount”, “discounts”, “discounted” and “discounting” are all replaced with “discount”. Sometimes, the Stemmer actually strips off additional characters from the end, so “include”, “includes”, “included”, and “including” are all replaced with “includ”.


Removal of non-words: Non-words and punctuation have been removed. All white spaces (tabs, newlines, spaces) have all been trimmed to a single space character.


   proecssEmail.m에서 다음과 같은 이메일 사전 처리 및 정규화를 구현합니다. 


Lower-casing (소문자로 변환): 전체 이메일이 소문자로 변환되어 대문자 표시를 무시합니다. 예를 들어, IndIcaTE는 Indicate와 동일하게 처리)


Stripping HTML (HTML 제거): 모든 HTML 태그가 이메일에서 제거됩니다. 대부분의 이메일에 HTML 형식을 제공합니다. 콘텐츠만 남고 모든 HTML 태그를 제거합니다.


Normalizing URLs (URL 정규화): 모든 URL을 'httpaddr' 텍스트로 변환


Normalizing Email Addresses (이메일 주소 정규화): 모든 이메일 주소가 'emailaddr' 텍스트로 변환합니다. 


Normalizing  Numbers(숫자 정규화): 모든 숫자는 'number' 텍스트로 변환합니다. 


Normalizing dollars(달러 정규화): 모든 달러는 'dollars' 텍스트로 변환합니다. 


Word Stemming (단어 스태밍): 단어를 어간 형태로 축소합니다. 예를 들어, “discount”, “discounts”, “discounted”, “discounting”를 “discount”로 대체합니다. 경우에 따라 형태소 분석기는 실제로 끝에서 추가 문자를 제거하므로 “include”, “includes”, “included”,  “including”는 모두 “includ”로 대체합니다. 


Removal of non-words(비 단어 제거): 단어가 아닌 느낌표와 구두점을 제거합니다. 탭, 줄 바꿈, 공백 등은 한 칸  공백으로 대체합니다. 



   The result of these preprocessing steps is shown in Figure 9. While pre- processing has left word fragments and non-words, this form turns out to be much easier to work with for performing feature extraction.


   그림 9는 전처리 과정을 거친 이메일입니다. 전처리 과정에서 단어 조각과 비 단어가 남았지만 형식은 피처를 추출하는 데 훨씬 더 쉽게 되어 있습니다. 


2.1.1 Vocabulary List (단어 목록)


   After preprocessing the emails, we have a list of words (e.g., Figure 9) for each email. The next step is to choose which words we would like to use in our classifier and which we would want to leave out.

   For this exercise, we have chosen only the most frequently occuring words as our set of words considered (the vocabulary list). Since words that occur rarely in the training set are only in a few emails, they might cause the model to overfit our training set. The complete vocabulary list is in the file vocab.txt and also shown in Figure 10. Our vocabulary list was selected by choosing all words which occur at least a 100 times in the spam corpus, resulting in a list of 1899 words. In practice, a vocabulary list with about 10,000 to 50,000 words is often used.

   Given the vocabulary list, we can now map each word in the preprocessed emails (e.g., Figure 9) into a list of word indices that contains the index of the word in the vocabulary list. Figure 11 shows the mapping for the sample email. Specifically, in the sample email, the word “anyone” was first normalized to “anyon” and then mapped onto the index 86 in the vocabulary list.

   Your task now is to complete the code in processEmail.m to perform this mapping. In the code, you are given a string str which is a single word from the processed email. You should look up the word in the vocabulary list vocabList and find if the word exists in the vocabulary list. If the word exists, you should add the index of the word into the word indices variable. If the word does not exist, and is therefore not in the vocabulary, you can skip the word.

   Once you have implemented processEmail.m, the script ex6 spam.m will run your code on the email sample and you should see an output similar to Figures 9 & 11.


   이메일을 전 처리한 후 각 이메일에 대한 단어 목록이 있습니다. 다음 과정은 분류기에서 사용할 단어와 생략할 단어를 선택하는 것입니다.

   실습에서 가장 자주 사용하는 단어를 단어 목록으로 만들었습니다. 학습 셋에서 드물게 사용하는 단어는 모델이 학습 셋에 과적합할 수 있기 때문에 제거합니다. 전체 어휘 목록은 vocab.txt 파일에 있으며 그림 10과 같습니다. 단어 목록은 전처리된 스팸 이메일에서 100번 이상 나오는 모든 1899개의 단어를 목록화하였습니다. 실제로 약 10,000 ~ 50,000 개의 단어 목록을 주로 사용합니다. 

   전처리된 이메일은 단어 목록의 단어 색인으로 매핑합니다. 그림 11은 샘플 이메일의 매핑을 나타냅니다. 샘플 이메일에서 'anyone'이라는 단어는 'anyon'으로 정규화된 다음 단어 목록의 단어 색인 86에 매핑합니다. 

   이번 실습은 매핑을 수행하기 위해 processEmail.m 코드를 완성하는 것입니다. 코드에서 처리된 이메일의 단어인 str 문자열을 제공합니다. 단어 목록 vocabList에서 단어를 검색하고 단어가 단어 목록에 있는 지를 찾습니다. 단어가 있다면 단어 색인을 추가합니다. 단어가 단어 목록에 없다면 해당 단어는 건너뜁니다. 

   processEmail.m을 구현하면 ex6_spam.m 파일은 이메일 샘플 코드를 실행하여 그림 9와 그림 11과 유사한 출력을 표시합니다. 



Octave/MATLAB Tip: In Octave/MATLAB, you can compare two strings with the strcmp function. For example, strcmp(str1, str2) will return 1 only when both strings are equal. In the provided starter code, vocabList is a “cell-array” containing the words in the vocabulary. In Octave/MATLAB, a cell-array is just like a normal array (i.e., a vector), except that its elements can also be strings (which they can’t in a normal Octave/MATLAB matrix/vector), and you index into them using curly braces instead of square brackets. Specifically, to get the word at index i, you can use vocabList{i}. You can also use length(vocabList) to get the number of words in the vocabulary.


Octave/MATLAB Tip: strcmp 함수는 두 문자열을 비교합니다. 예를 들어, strcmp(str1, str2)는 두 문자열이 같으면 1을 반환합니다. vocabList.m 파일의 단어는 Cell-array 형태인 벡터와 같습니다.  해당 요소는 문자열이거나 인덱스입니다. 대괄호 대신 중괄호를 사용하십시오. 인덱스 i에서 단어를 얻으려면 vocabList{i} 를 사용합니다. length(vocabList)는 단어 목록에서 단어의 개수를 반환합니다. 


< Part 1: 이메일 전처리>


(1) 데이터 업로드


clear ;             % 옥타브 프로그램에 모든 변수를 제거

close all;         % 터미널 이외의 창을 닫음

clc                   % 터미널을 깨끗이 정리 


(2) readFile.m 파일 분석

function file_contents = readFile(filename)

%READFILE 지정된 파일을 로드하고 전체 내용을 반환 

%   file_contents = READFILE(filename) 

%       지정된 파일의 내용을 file_contents 변수에 저장

%


% 로드

fid = fopen(filename);

if fid

    file_contents = fscanf(fid, '%c', inf);

    fclose(fid);

else

    file_contents = '';

    fprintf('Unable to open %s\n', filename);

end


end



   fopen 명령어는 파일을 열고 3보다 큰 파일 ID를 반환합니다. 만일 파일을 열수 없다면 -1을 반환합니다. fopen 명령어로 파일을 열고 fclose 명령어로 파일을 닫습니다. fopen('all')은 모든 열린 파일의 ID를 반환하고, fclose('all')은 모든 열린 파일을 닫습니다.  


   fscanf 명령어는 텍스트 파일에서 데이터를 읽습니다. 데이터를 읽을 때 몇 가지 옵션이 있습니다. inf는 모든 데이터를 읽는다는 의미입니다.


   따라서,  fopen 명령어로 파일을 열고 fscanf 명령어로 파일을 읽고, fclose 명령어로 파일을 닫습니다. 


>> fid = fopen('emailSample1.txt')

fid =  18


>> file_contents = fscanf(fid, '%c', inf)

file_contents = > Anyone knows how much it costs to host a web portal ?

>

Well, it depends on how many visitors you're expecting.

This can be anywhere from less than 10 bucks a month to a couple of $100.

You should checkout http://www.rackspace.com/ or perhaps AmazonEC2

if youre running something big..


To unsubscribe yourself from this mailing list, send an email to:

groupname-unsubscribe@egroups.com



>> fclose(18)                      % 파일을 닫음

ans = 0

>> file_contents = fscanf(fid, '%c', inf)     % 더 이상 파일을 열 수 없음

error: fscanf: invalid stream number = 18

>> fopen ('all')           

ans = [](0x0)


   간단하게 파일의 내용을 보고 싶을 때는 type 명령어를 사용합니다. 


>> ls

ex6.m                           spamSample1.txt

ex6.mlx                         spamSample2.txt


>> type spamSample1.txt

Do You Want To Make $1000 Or More Per Week?

...


   readFile.m 파일은 단순하게 텍스트 파일을 로드하고 읽어 들입니다. 샘플 이메일의 내용을 텍스트로 읽은 후에 file_contents 변수에 저장합니다.



(3) getVocabList.m 파일 분석



function vocabList = getVocabList()

% GETVOCABLIST  vocab.txt 파일의 단어 목록을 읽고 단어 셀 배열로 반환

%   vocabList = GETVOCABLIST() 

%       vocab.txt 파일의 단어 목록을 읽고 단어 셀 배열로 반환


% 단어 목록 읽기 

fid = fopen('vocab.txt');


% 셀 배열 안에 단어 목록의 단어 개수

n = 1899;  % 단어 목록의 단어 개수 


% 문자열을 단어 목록의 인덱스로 변환

% 실제로 해시 맵 포맷을 사용 

vocabList = cell(n, 1);

for i = 1:n

    % Word Index (can ignore since it will be = i)

    fscanf(fid, '%d', 1);

    % Actual Word

    vocabList{i} = fscanf(fid, '%s', 1);

end

fclose(fid);


end


 특정 크기의 셀형 배열을 만들기 위해 cell 함수를 사용합니다. 


>> cell(3,1)       

ans =

{

  [1,1] = [](0x0)

  [2,1] = [](0x0)

  [3,1] = [](0x0)

}


>> cell (3,3)

ans =

{

  [1,1] = [](0x0)

  [2,1] = [](0x0)

  [3,1] = [](0x0)

  [1,2] = [](0x0)

  [2,2] = [](0x0)

  [3,2] = [](0x0)

  [1,3] = [](0x0)

  [2,3] = [](0x0)

  [3,3] = [](0x0)


   이 파일은 vocab.txt 파일을 직접 불러들이지 않고 셀 배열로 변환합니다. 


>> type vocab.txt  

1       aa

2       ab

3       abil

4       abl

5       about

6       abov

7       absolut

8       abus

9       ac

10      accept

...


>> n

n =  1899

>> for i = 1:n

fscanf(fid, '%d', 1);

vocabList{i} = fscanf(fid, '%s',1);

end


>> vocabList

>> vocabList

vocabList =

{

  [1,1] = aa

  [2,1] = ab

  [3,1] = abil

  [4,1] = abl

  [5,1] = about

  [6,1] = abov

  [7,1] = absolut

  [8,1] = abus

  [9,1] = ac

  [10,1] = accept

...



(4) processEmail.m 파일 분석



function word_indices = processEmail(email_contents)

%PROCESSEMAIL 이메일의 내용을 전처리 

%   word_indices = PROCESSEMAIL(email_contents) preprocesses 

%       이메일 내용을 전처리하고 이메일에 포함된 단어를 단어 목록의 인덱스 값으로 반환

%


% 단어 목록 로드

vocabList = getVocabList();


% 반환 값 초기화 

word_indices = [];


% =============== 이메일 전처리 l ===========================


% 소문자로 변환

email_contents = lower(email_contents);


% 모든 html 제거 

% html은 < 로 시작해서 >로 끝나고 태그 사이에 스페이스를 둠. 예, < or >

email_contents = regexprep(email_contents, '<[^<>]+>', ' ');


% 숫자 정규화  

% 0-9 사이의 숫자를 찾아서 number 텍스트로 대체 

email_contents = regexprep(email_contents, '[0-9]+', 'number');


% URL 정규화 

% http:// 또는 https:// 로 시작하는 문자열을 찾아서 httpaddr 텍스트로 대체

email_contents = regexprep(email_contents, '(http|https)://[^\s]*', 'httpaddr');


% Email 주소 정규화 

%  중간에 @ 가 있는 문자열을 찾아서 emailaddr 텍스트로 대체 

email_contents = regexprep(email_contents, '[^\s]+@[^\s]+', 'emailaddr');


% 달러($) 정규화  

email_contents = regexprep(email_contents, '[$]+', 'dollar');



% ================== 이메일 토큰화 =====================


% 이메일 전처리 완료를 표시 

fprintf('\n==== Processed Email ====\n\n');


% 파일 처리 

l = 0;


while ~isempty(email_contents)


    % 구두점을 제외하고 한 단어씩 str로 보냄  

    [str, email_contents] = strtok(email_contents, ...

              [' @$/#.-:&*+=[]?!(){},''">_<;%' char(10) char(13)]);   


    % 알파벳 또는 숫자 아닌 것들은 제거 

    str = regexprep(str, '[^a-zA-Z0-9]', '');


    % 어간화  

    % (porterStemmer는 간혹 문제를 일으킬 수 있으므로 catch block을 사용)

    try str = porterStemmer(strtrim(str)); 

    catch str = ''; continue;

    end;


    % 너무 짧은 단어들은 스킵 

    if length(str) < 1

       continue;

    end


    % 단어 목록에서 단어를 찾은 후에 단어 인덱스를 추가하라 

    % 

    % ====================== YOUR CODE HERE ======================

    % Instructions: 단어 목록에 있는 단어가 있다면 단어의 인덱스를 반환 

   %            str = 'action'이라면, 단어 목록을 뒤져서 'action'이라는 단어의 위치를 찾음 

   %            예를 들어, vocabList{18} = 'action' 이므로 word_indices는 18 값을 반환

   %            word_indices = [word_indices ; 18]; ).

    % 

    % Note: vocabList{idx} 는 단어 목록의 index를 반환

    % 

    % Note: strcmp(str1, str2) 함수를 이용하여 두 문자열을 비교 

    %            두 문자열이 같다면 1을 반환 

    %





    % =============================================================



    % 스크린에 표시 

    if (l + length(str) + 1) > 78

        fprintf('\n');

        l = 0;

    end

    fprintf('%s ', str);

    l = l + length(str) + 1;


end


% Print footer

fprintf('\n\n=========================\n');


end



   email_contents에 들어있는 이메일 내용을 전 처리합니다. 


모든 글자를 소문자로 변환

   모든 글자를 소문자로 변환할 때 lower 함수를 사용하고, 대문자로 변환할 때 upper 함수를 사용합니다. 


>> email_contents

email_contents = > Anyone knows how much it costs to host a web portal ?

>

Well, it depends on how many visitors you're expecting.

This can be anywhere from less than 10 bucks a month to a coupleof $100.

You should checkout http://www.rackspace.com/ or perhaps Amazon EC2

if youre running something big..


To unsubscribe yourself from this mailing list, send an email to:

groupname-unsubscribe@egroups.com



>>  upper(email_contents)

ans = > ANYONE KNOWS HOW MUCH IT COSTS TO HOST A WEB PORTAL ?

>

WELL, IT DEPENDS ON HOW MANY VISITORS YOU'RE EXPECTING.

THIS CAN BE ANYWHERE FROM LESS THAN 10 BUCKS A MONTH TO A COUPLEOF $100.

YOU SHOULD CHECKOUT HTTP://WWW.RACKSPACE.COM/ OR PERHAPS AMAZON EC2

IF YOURE RUNNING SOMETHING BIG..


TO UNSUBSCRIBE YOURSELF FROM THIS MAILING LIST, SEND AN EMAIL TO:

GROUPNAME-UNSUBSCRIBE@EGROUPS.COM


>> email_contents = lower(email_contents)

ans = > anyone knows how much it costs to host a web portal ?

>

well, it depends on how many visitors you're expecting.

this can be anywhere from less than 10 bucks a month to a coupleof $100.

you should checkout http://www.rackspace.com/ or perhaps amazon ec2

if youre running something big..


to unsubscribe yourself from this mailing list, send an email to:

groupname-unsubscribe@egroups.com


HTML 제거

   HTML 태그는 <와 > 사이에 쓰여지기 때문에 이런 것을 제거합니다. 


>> email_contents = regexprep(email_contents, '<[^<>]+>', ' ')

email_contents = > anyone knows how much it costs to host a web portal ?

>

well, it depends on how many visitors you're expecting.

this can be anywhere from less than 10 bucks a month to a coupleof $100.

you should checkout http://www.rackspace.com/ or perhaps amazon ec2

if youre running something big..


to unsubscribe yourself from this mailing list, send an email to:

groupname-unsubscribe@egroups.com

>>



숫자 정규화 

   숫자를 number 텍스트로 대체


>> email_contents = regexprep(email_contents, '[0-9]+', 'number')

email_contents = > anyone knows how much it costs to host a web portal ?

>

well, it depends on how many visitors you're expecting.

this can be anywhere from less than number bucks a month to a couple of $number.

you should checkout http://www.rackspace.com/ or perhaps amazon ecnumber

if youre running something big..


to unsubscribe yourself from this mailing list, send an email to:

groupname-unsubscribe@egroups.com


URL 정규화

   HTTP 또는 HTTPS:// 로 시작하는 문자열을 httpaddr 문자열로 대체



>> email_contents = regexprep(email_contents, '(http|https)://[^\*', 'httpaddr')

email_contents = > anyone knows how much it costs to host a web portal ?

>

well, it depends on how many visitors you're expecting.

this can be anywhere from less than number bucks a month to a couple of $number.

you should checkout httpaddr or perhaps amazon ecnumber

if youre running something big..


to unsubscribe yourself from this mailing list, send an email to:

groupname-unsubscribe@egroups.com


이메일 주소 정규화

   중간에 @ 가 있는 문자열을 찾아서 emailaddr 텍스트로 대체


>> email_contents = regexprep(email_contents, '[^\s]+@[^\s]+', 'eiladdr')

email_contents = > anyone knows how much it costs to host a web portal ?

>

well, it depends on how many visitors you're expecting.

this can be anywhere from less than number bucks a month to a couple of $number.

you should checkout httpaddr or perhaps amazon ecnumber

if youre running something big..


to unsubscribe yourself from this mailing list, send an email to:

emailaddr



달러($) 정규화


>> email_contents = regexprep(email_contents, '[$]+', 'dollar')

email_contents = > anyone knows how much it costs to host a web portal ?

>

well, it depends on how many visitors you're expecting.

this can be anywhere from less than number bucks a month to a couple of dollarnumber.

you should checkout httpaddr or perhaps amazon ecnumber

if youre running something big..


to unsubscribe yourself from this mailing list, send an email to:

emailaddr



(5) 전처리된 이메일에서 첫 단어를 분리하기 


   email_contents 에 있는 내용의 첫 단어를  str 값으로 반환하고 email_contents에서 제거합니다.  


   

 [str, email_contents] = strtok(email_contents, ...

              [' @$/#.-:&*+=[]?!(){},''">_<;%' char(10) char(13)]



>>  [str, email_contents] = strtok(email_contents, ...

              [' @$/#.-:&*+=[]?!(){},''">_<;%' char(10) char(13)]


>> str

str = anyone


>> email_contents    %anyone 단어를 제거함

email_contents =  knows how much it costs to host a web portal ?

>

well, it depends on how many visitors you're expecting.

this can be anywhere from less than 10 bucks a month to a coupleof $100.

you should checkout http://www.rackspace.com/ or perhaps amazon ec2

if youre running something big..


to unsubscribe yourself from this mailing list, send an email to:

groupname-unsubscribe@egroups.com



  'while ~isempty(email_contents)'

   while 구문의 조건은 email_contents 의 마지막까지 반복합니다. email_contents의 단어를 계속 제거하다 보면 더 이상 단어가 없는 마지막까지 진행되는 상황이 됩니다.  첫 단어 anyone을 제거한 상황에서는 0이지만, ~ 표시를 이용하여 참이 됩니다. 마지막까지 진행하면 거짓이 되어 while 구문을 탈출합니다


>> isempty(email_contents)

ans = 0

>> ~isempty(email_contents)

ans =  1

 

   (6) 단어와 단어 목록 비교


   vocabList 는 1899 X 1차원 벡터이고, str은 한 단어입니다.  벡터의 모든 단어와 비교하여 같은 단어가 있을 때 1의 값을 반환합니다. 따라서 1을 반환하는 경우를 찾기 위한 조건문은 strcmp(vocabList, str) == 1입니다. 


>> vocabList = getVocabList();

>> str

str = anyone

>> strcmp(vocabList, str)

ans =

   0

   0

   ...

   0



따라서, 만일 str의 값이 action일 경우 다음과 같이 find 함수는 인덱스를 반환합니다.


>> str = 'action'

str = action

>> find(strcmp(vocabList, str) ==1)

ans =  18

>> index = find(strcmp(vocabList, str) == 1)

index =  18


 그리고, 반환해야 할 word_indices  변수에 값을 넣습니다. 


 word_indices = [word_indices; index]


(6) processEmail.m 파일에 정답 입력


function word_indices = processEmail(email_contents)

%PROCESSEMAIL 이메일의 내용을 전처리 

%   word_indices = PROCESSEMAIL(email_contents) preprocesses 

%       이메일 내용을 전처리하고 이메일에 포함된 단어를 단어 목록의 인덱스 값으로 반환

%


% 단어 목록 로드

vocabList = getVocabList();


% 반환 값 초기화 

word_indices = [];


% =============== 이메일 전처리 l ===========================


% 소문자로 변환

email_contents = lower(email_contents);


% 모든 html 제거 

% html은 < 로 시작해서 >로 끝나고 태그 사이에 스페이스를 둠. 예, < or >

email_contents = regexprep(email_contents, '<[^<>]+>', ' ');


% 숫자 정규화  

% 0-9 사이의 숫자를 찾아서 number 텍스트로 대체 

email_contents = regexprep(email_contents, '[0-9]+', 'number');


% URL 정규화 

% http:// 또는 https:// 로 시작하는 문자열을 찾아서 httpaddr 텍스트로 대체

email_contents = regexprep(email_contents, '(http|https)://[^\s]*', 'httpaddr');


% Email 주소 정규화 

%  중간에 @ 가 있는 문자열을 찾아서 emailaddr 텍스트로 대체 

email_contents = regexprep(email_contents, '[^\s]+@[^\s]+', 'emailaddr');


% 달러($) 정규화  

email_contents = regexprep(email_contents, '[$]+', 'dollar');



% ================== 이메일 토큰화 =====================


% 이메일 전처리 완료를 표시 

fprintf('\n==== Processed Email ====\n\n');


% 파일 처리 

l = 0;


while ~isempty(email_contents)


    % 구두점을 제외하고 한 단어씩 str로 보냄  

    [str, email_contents] = strtok(email_contents, ...

              [' @$/#.-:&*+=[]?!(){},''">_<;%' char(10) char(13)]);   


    % 알파벳 또는 숫자 아닌 것들은 제거 

    str = regexprep(str, '[^a-zA-Z0-9]', '');


    % 어간화  

    % (porterStemmer는 간혹 문제를 일으킬 수 있으므로 catch block을 사용)

    try str = porterStemmer(strtrim(str)); 

    catch str = ''; continue;

    end;


    % 너무 짧은 단어들은 스킵 

    if length(str) < 1

       continue;

    end


    % 단어 목록에서 단어를 찾은 후에 단어 인덱스를 추가하라 

    % 

    % ====================== YOUR CODE HERE ======================

    % Instructions: 단어 목록에 있는 단어가 있다면 단어의 인덱스를 반환 

   %            str = 'action'이라면, 단어 목록을 뒤져서 'action'이라는 단어의 위치를 찾음 

   %            예를 들어, vocabList{18} = 'action' 이므로 word_indices는 18 값을 반환

   %            word_indices = [word_indices ; 18]; ).

    % 

    % Note: vocabList{idx} 는 단어 목록의 index를 반환

    % 

    % Note: strcmp(str1, str2) 함수를 이용하여 두 문자열을 비교 

    %            두 문자열이 같다면 1을 반환 

    %


index = find(strcmp(vocabList, str) == 1);

word_indices = [word_indices; index];



    % =============================================================



    % 스크린에 표시 

    if (l + length(str) + 1) > 78

        fprintf('\n');

        l = 0;

    end

    fprintf('%s ', str);

    l = l + length(str) + 1;


end


% Print footer

fprintf('\n\n=========================\n');


end



>> word_indices = processEmail(file_contents)


==== Processed Email ====


anyon know how much it cost to host a web portal well it depend on how mani

visitor you re expect thi can be anywher from less than number buck a month

to a coupl of dollarnumb you should checkout httpaddr or perhap amazon ecnumb

if your run someth big to unsubscrib yourself from thi mail listsend an

email to emailaddr


=========================

word_indices =


     86

    916

    794

   1077

    883

    370

   1699

    790

   1822

   1831

    883

    431

   1171

    794

   1002

   1893

   1364

    592

   1676

    238

    162

     89

    688

    945

   1663

   1120

   1062

   1699

    375

   1162

    479

   1893

   1510

    799

   1182

   1237

    810

   1895

   1440

   1547

    181

   1699

   1758

   1896

    688

   1676

    992

    961

   1477

     71

    530

   1699

    531


<결과 확인>



매거진의 이전글 머신러닝 옥타브 실습(6-3): 서포트 벡터 머신 구현
브런치는 최신 브라우저에 최적화 되어있습니다. IE chrome safari