<jupyter_start><jupyter_text>Assignment 2 - DS4Biz Y63 TextScraping_Classification*** Team Detail**Team Name**: sompinandsomshine   *** *Student 1***Student ID**: 61070278   **Student Full Name**: นายกิตติภณ สุรุ่งเรืองสกุล*** *Student 2***Student ID**: 61070330   **Student Full Name**: นางสาวอิงฟ้า ภูติวรนาถ*** import package ที่ต้องใช้- requests, bs4 ใช้ scrape ข้อมูลจากหน้าเว็บ- pandas(pd), numpy(np) ใช้จัดการข้อมูลที่ scrape มาเช่น ทำเป็น DataFrame, แปลงเป็น csv/txt- warnings ใช้ปิด warning ที่แจ้งเตือน- CountVectorizer, TfidfTransformer, TfidfVectorizer ใช้เตรียมข้อมูลเช่น ทำ term weighting- nltk ใช้ในการประมวลผลภาษาธรรมชาติเช่น กำจัด stopwords (คำที่เกิดขึ้นบ่อยและไม่มีอิทธิพลในการจำแนก)- Pipeline ทำให้การทำงานเป็นลำดับและนำมาใช้กับ GridSearchCV- train_test_split, cross_val_score, StratifiedKFold, GridSearchCV นำมาทำ model selection และหา best hyperparameter- neighbors(KNeighborsClassifier), xgboost(xgb), ensemble(RandomForestClassifier) model ที่จะนำมาใช้ในการทำนายว่าแต่ข่าวเป็นประเภทไหน<jupyter_code>import requests
import bs4

import pandas as pd
import numpy as np

import warnings
warnings.filterwarnings('ignore')

from sklearn.feature_extraction.text import CountVectorizer
from sklearn.feature_extraction.text import TfidfTransformer
from sklearn.feature_extraction.text import TfidfVectorizer

import nltk
from nltk.corpus import stopwords

from sklearn.model_selection import train_test_split
from sklearn.model_selection import StratifiedKFold
from sklearn.model_selection import GridSearchCV
from sklearn.model_selection import cross_val_score
from sklearn.metrics import accuracy_score

from sklearn.neighbors import KNeighborsClassifier
import xgboost as xgb
from sklearn.ensemble import RandomForestClassifier

import matplotlib.pyplot as plt<jupyter_output><empty_output><jupyter_text>Data Collectionlink: http://www.it.kmitl.ac.th/~teerapong/news_archive/index.html ใช้ **requests.get** ตรวจสอบว่าลิงก์ที่ต้องการ scrape ข้อมูลสามารถเข้าได้มั้ย<jupyter_code>response = requests.get('http://www.it.kmitl.ac.th/~teerapong/news_archive/index.html') #ใส่ลิงก์ที่เราต้องการ scrape
print(response) # response 200 คือสามารถเข้าถึงได้ พร้อมดึงข้อมูล<jupyter_output><Response [200]><jupyter_text>ใช้ **bs4.BeautifulSoup** ในการอ่าน code ของลิงก์ที่เราใส่ไป<jupyter_code>html_page = bs4.BeautifulSoup(response.content, 'html.parser') #เก็บ code ที่ได้ไว้ในตัวแปร html_page
print(html_page)
#ต้องการดึงลิงก์ข่าวของแต่ละเดือน
selector = 'body > div > div.main > ul > li> a' #ลิงก์ข่าวของแต่ละเดือนอยู่ใน tags ul>li>a จึงกำหนดให้ไปดึง code ที่อยู่ในส่วนนั้นออกมา
tags = html_page.select(selector) #โดยให้ return code ที่ตรงกับ selector ที่กำหนดไว้ทั้งหมดและเก็บไว้ในตัวแปร tags
tags #เรียกดูผลลัพธ์ว่าตรงกับที่ต้องการมั้ย<jupyter_output><empty_output><jupyter_text>ได้ code ที่เป็นลิงก์มาหมดแล้วแต่ยังไม่พร้อมใช้งานเพราะ ลิงก์ที่ต้องการมีหน้าตาแบบนี้ http://www.it.kmitl.ac.th/~teerapong/news_archive/month-jan-2017.html จึงจะทำการสร้าง **list_archive** เพื่อรอเก็บลิงก์ที่พร้อมใช้งาน<jupyter_code>list_archive = [] #list ที่รอเก็บลิงก์ที่จะนำไปใช้งานต่อ
for tag in tags: #ทำการวน for loop จากตัวแปร tags ที่เก็บ code ที่มีลิงก์อยู่
    list_archive.append('http://www.it.kmitl.ac.th/~teerapong/news_archive/' + tag['href'])
    '''นำ tag['href'] ซึ่งคือส่วนที่อยู่ใน href เท่านั้น (ex. month-jan-2017.html) มาต่อกับ http://www.it.kmitl.ac.th/~teerapong/news_archive/ 
    เพื่อให้ได้ลิงก์ตามที่เราต้องการ แล้วลิงก์นั้นไปเก็บไว้ใน list_archive ที่สร้างเอาไว้'''
list_archive #เรียกดูผลลัพธ์ว่าตรงกับที่ต้องการมั้ย<jupyter_output><empty_output><jupyter_text>Create functionสร้าง Function ที่ใช้ในการ scrape- category ประเภทข่าว -> extract_category()- title หัวข้อข่าว -> extract_title()- link ลิงก์ข่าว -> extract_link_title()<jupyter_code># สร้าง Function ที่ใช้ในการ scrape category
def extract_category(html_page):
    selector = 'body > div > div.main > table > tbody > tr> td.category' #กำหนดให้ดึงค่าที่อยู่ในตารางในส่วนของคอลัมน์  Article Category
    tags = html_page.select(selector)
    category = [] #สร้าง list ชื่อ category ไว้รอเก็บค่าที่ scrape ได้

    for tag in tags: #ทำการวน for loop จากตัวแปร tags
        if tag.text.strip() != 'N/A': #จะเอาข้อมูล category ใส่ใน list ชื่อ category ถ้าข้อมูลที่ดึงมาไม่เป็น N/A (ค่าว่าง)
            category.append(tag.text.strip()) #เพิ่มข้อมูลที่ scrape ได้ใส่ไว้ใน list ชื่อ category

    return category
# สร้าง Function ที่ใช้ในการ scrape title
def extract_title(html_page):
    selector = 'body > div > div.main > table > tbody > tr > td.title > a' 
    #กำหนดให้ดึงค่าที่อยู่ในตารางในส่วนของคอลัมน์  Article Title และดึงไปที่ tag a เพราะแถวที่มีข้อมูลจะเป็น tag a แต่ถ้าไม่มีข้อมูลแล้วจะเป็น tag i
    tags = html_page.select(selector)
    title = [] #สร้าง list ชื่อ title ไว้รอเก็บค่าที่ scrape ได้
    
    for tag in tags: #ทำการวน for loop จากตัวแปร tags
        title.append(tag.text.strip()) #เพิ่มข้อมูลที่ scrape ได้ใส่ไว้ใน list ชื่อ title
        
    return title
# สร้าง Function ที่ใช้ในการ scrape link
def extract_link_title(html_page):
    selector = 'body > div > div.main > table > tbody > tr > td.title > a'
    #กำหนดให้ดึงค่าที่อยู่ในตารางในส่วนของคอลัมน์  Article Title และดึงไปที่ tag a เพราะลิงก์ของข่าวอยู่ tag a
    tags = html_page.select(selector)
    link_title = [] #สร้าง list ชื่อ link_title ไว้รอเก็บค่าที่ scrape ได้
    
    for tag in tags: #ทำการวน for loop จากตัวแปร tags
        link_title.append('http://www.it.kmitl.ac.th/~teerapong/news_archive/' + tag['href'])
        #ทำการแก้ไขข้อมูลให้เป็นลิงก์ที่พร้อมใช้งาน แล้วค่อยเพิ่มข้อมูลใส่ไว้ใน list ชื่อ link_title
        
    return link_title
#ตัวอย่าง
url = 'http://www.it.kmitl.ac.th/~teerapong/news_archive/month-jan-2017.html'
response = requests.get(url)
html_page = bs4.BeautifulSoup(response.content, 'html.parser')
print('example:\n', extract_category(html_page)[0],'\n', extract_title(html_page)[0],'\n', extract_link_title(html_page)[0])<jupyter_output>example:
 technology 
 21st-Century Sports: How Digital Technology Is Changing the Face Of The Sporting Industry 
 http://www.it.kmitl.ac.th/~teerapong/news_archive/article-jan-0418.html<jupyter_text>สร้าง Function **extract_news_archive_info()** สำหรับเรียกใช้งาน Function ในการ scrape category ประเภทข่าว, title หัวข้อข่าว, link ลิงก์ข่าว ทั้งหมด โดยใส่แค่ลิงก์เดือนก็จะดึง category ประเภทข่าว, title หัวข้อข่าว, link ลิงก์ข่าว ในเดือนนั้นออกมาทั้งหมด และจะ return กลับมาในรูปของ DataFrame<jupyter_code>def extract_news_archive_info(url): #รับลิงก์ของเดือนที่ต้องการดึงข้อมูล
    response = requests.get(url)
    html_page = bs4.BeautifulSoup(response.content, 'html.parser') #ดึงหน้าเว็บของลิงก์นั้น
    
    #เอาหน้าเว็บที่ดึงออกมาไปใส่ใน argument แต่ละ Function ที่ได้สร้างไว้เพื่อดึงข้อมูล ผลลัพธ์ที่ได้กลับมาจะอยู่ในรูปของ list
    category = extract_category(html_page)
    title = extract_title(html_page)
    link_title = extract_link_title(html_page)

    #เอาข้อมูลที่อยู่ในรูปของ list ที่ได้กลับมามาแปลงให้อยู่ในรูปของ pandas Series เพื่อที่จะได้เอาข้อมูลทั้ง 3 ตัวมา concat กันให้เป็น DataFrame
    category = pd.Series(category)
    title = pd.Series(title)
    link_title = pd.Series(link_title)
    
    #เอาข้อมูลทั้ง 3 ตัวที่อยู่ในรูปของ pandas Series แล้วมา concat กันในแนวคอลัมน์ (เอาคอลัมน์มาต่อๆกัน)
    result = pd.concat([category, title, link_title], axis=1)
    #rename ชื่อ column ให้ตรงกับที่ต้องการใช้งาน โดยจะเปลี่ยนชื่อเป็น category-ประเภทข่าว, title-หัวข้อข่าว, link-ลิงก์ข่าว
    result = result.rename(columns={0: "category", 1: "title", 2: "link"})
    return result #return DataFrame
#ตัวอย่าง อันนี้คือ DataFrame ที่ Function extract_news_archive_info() return กลับมา
#ข้อมูลที่ return กลับมาจะเป็นข่าวทั้งหมดในเดือนที่เราส่งไป
url = 'http://www.it.kmitl.ac.th/~teerapong/news_archive/month-jan-2017.html'
extract_news_archive_info(url)<jupyter_output><empty_output><jupyter_text>Create DataFrameสร้าง DataFrame เพื่อรอรับข้อมูลที่ scrape ได้ซึ่งข้อมูลที่เราจะ scrape คือลิงก์ข่าวในแต่ละเดือนใน list_archive โดยเราจะ scrape category ประเภทข่าว, title หัวข้อข่าว, link ลิงก์ข่าว<jupyter_code>#สร้าง DataFrame ที่รอรับข้อมูลที่ scrape ได้และกำหนดชื่อ column ตามสิ่งที่จะ scrape
df = pd.DataFrame(columns=['category', 'title', 'link'])
df #ได้ df เปล่าๆที่รอรับข้อมูลที่ scrape ได้<jupyter_output><empty_output><jupyter_text>เอา list_archive (ลิงก์เดือนทั้งหมด) ไปเข้า Function extract_news_archive_info()ขั้นตอนนี้ทำเพื่อให้ได้ข่าวทั้งหมดในทุกๆเดือนมารวมกันเป็น DataFrame อันเดียว<jupyter_code>list_archive<jupyter_output><empty_output><jupyter_text>เอาแต่ละลิงก์ใน list_archive ไปเข้า function extract_news_archive_info() และเพิ่มเข้าไปใน DataFrame ที่เราเตรียมไว้<jupyter_code>for url in list_archive: #วน for loop เพื่อเอาลิงก์เดือนทีละอันไปเข้า function
    result = extract_news_archive_info(url) #เอาลิงก์เดือนไปเข้า function และเก็บ DataFrame ที่ return กลับมาในตัวแปร result
    df = df.append(result, ignore_index=True) #เอา result ที่ได้จาก function ไปเพิ่มใน DataFrame ที่สร้างรอไว้
df #หน้าตาของ DataFrame ที่มีข้อมูล category ประเภทข่าว, title หัวข้อข่าว, link ลิงก์ข่าว ทั้งหมด<jupyter_output><empty_output><jupyter_text>ลองเช็คค่า null<jupyter_code>df.isna().sum()
#ไม่มีค่า null เพราะ clean ไปตอน scrape ข้อมูลแล้ว<jupyter_output><empty_output><jupyter_text>Add id coloumnแต่ละข่าวมี id ของมันเองเลยจะทำการเก็บแยกเพิ่มอีกคอลัมน์<jupyter_code>#ในแต่ละลิงก์ข่าวจะมี id ของข่าวแฝงอยู่
df['link'].iloc[0]
#ใช้ index slicing ในการหา id ข่าว โดยจะเริ่มเก็บตั้งแต่ตัวที่ 50 ถึงตัวที่ 6 จากหลังสุด
df['id'] = df['link'].str[50:-5]
df<jupyter_output><empty_output><jupyter_text>Get Contentดึงข้อมูลในลิงก์ข่าว<jupyter_code>list_content = [] #สร้าง list ชื่อ list_content ไว้เก็บข้อมูล content ทั้งหมด<jupyter_output><empty_output><jupyter_text>สร้าง Function ที่ใช่ในการดึงข้อมูลข่าวในลิงก์ข่าวที่ใส่เข้าไป<jupyter_code>def get_content(url): #รับลิงก์ข่าวและเก็บไว้ในชื่อ url
    response = requests.get(url)
    html_page = bs4.BeautifulSoup(response.content, 'html.parser')

    selector = 'body > div > div.main > p' #เนื้อหาข่าวอยู่ในส่วน tag p
    #เนื่องจากเนื้อหาข่าวมีหลายย่อหน้า (1 ย่อหน้า : 1 tag p) จึงใช้ select ในการดึง ค่าที่ได้จะอยู่ในรูปของ list
    tags = html_page.select(selector) 

    content = '' #สร้างตัวแปร content รอเก็บเนื้อหาข่าวในรุปของ string
    for tag in tags: #วน for loop ในตัวแปร tags
        if tag.text.strip() != '': #ในเนื้อหาข่าวจะมีส่วนที่เป็นเว้นย่อหน้าเปล่าๆ ซึ่งเราไม่ต้องการเก็บส่วนนั้น จึงจะทำการเก็บเฉพาะส่วนที่ไม่ได้เว้นว่างไว้
            if '.html' in str(tag): #ในส่วนล่างสุดในบางข่าวจะมีลิงก์ให้กดกลับไปที่หน้าหลัก ซึ่งเราก็ไม่ต้องการเก็บเช่นกัน จึงจะทำการข้ามไป
                pass
            elif 'Comments are closed for this article' in str(tag): #ในส่วนล่างสุดในบางข่าวจะมีบอกว่าปิดไม่ให้มีการคอมเมนต์ ซึ่งเราก็ไม่ต้องการเก็บเช่นกัน จึงจะทำการข้ามไป
                pass
            else:
                content += tag.text.strip() + ' ' #หลังจากข้ามส่วนที่ไม่ต้องการเก็บไปหมดแล้วก็จะเพิ่มส่วนต้องการเก็บไว้ในตัวแปร content
                
    list_content.append(content.strip()) #หลังจากจบ loop ก็จะทำการเพิ่มเนื้อหาข่าวไปในตัวแปร list_content
#df['link'] คือลิงก์ข่าวทั้งหมด
print('example:', df['link'][0])
for i in range(len(df)): #วน for loop ตามความยาวทั้งหมดของ df
    get_content(df['link'].iloc[i]) #เอาลิงก์ข่าวใส่ไปใน function get_content()<jupyter_output><empty_output><jupyter_text>หลังจากวน for loop เพื่อลิงก์ข่าวใส่ไปใน function **get_content()** ครบหมดแล้ว ในตัวแปร list_content จะมีเนื้อหาข่าวทั้งหมด เราจะแปลง list_content จาก list ให้เป็น pandas Series เพื่อที่จะได้เอาไป concat กับ df ให้สมบูรณ์<jupyter_code>content = pd.Series(list_content) #แปลง list_content จาก list ให้เป็น pandas Series และเก็บไว้ในชื่อ content
df = pd.concat([df, content], axis=1) #เอา content ที่เป็น pandas Series มารวมกับ df ที่มีอยู่แล้วในแนวคอลัมน์ (เอาคอลัมน์มาต่อกัน)
df = df.rename(columns={0: "content"}) #rename ชื่อ column เพื่อให้สะดวกต่อการใช้งาน โดยจะเปลี่ยนชื่อเป็น content-เนื้อหาข่าว
df<jupyter_output><empty_output><jupyter_text>save DataFarame to csv/txtเพื่อให้ง่ายต่อการใช้ในภายหลัง<jupyter_code>df.to_csv('datastore/data.csv', index=False)
df = pd.read_csv('datastore/data.csv')
df.head()<jupyter_output><empty_output><jupyter_text>คัดให้เหลือเฉพาะที่ใช้ในการวิเคราะห์- AllArticles_OnlyHeading- AllArticles_OnlyContent- AllArticles_HeadingPlusContent- Category<jupyter_code>AllArticles_OnlyHeading = df['title']
AllArticles_OnlyContent = df['content']
AllArticles_HeadingPlusContent = df[['title','content']]
Category = df['category']<jupyter_output><empty_output><jupyter_text>**csv**save เป็นไฟล์ประเภท csv<jupyter_code>AllArticles_OnlyHeading.to_csv('datastore/AllArticles_OnlyHeading.csv', index=False)
AllArticles_OnlyContent.to_csv('datastore/AllArticles_OnlyContent.csv', index=False)
AllArticles_HeadingPlusContent.to_csv('datastore/AllArticles_HeadingPlusContent.csv', index=False)
Category.to_csv('target/category.csv', index=False)<jupyter_output><empty_output><jupyter_text>**txt**save เป็นไฟล์ประเภท txt<jupyter_code>AllArticles_OnlyHeading.to_csv(r'datastore/AllArticles_OnlyHeading.txt', header=None, index=None, sep=' ', mode='a')
AllArticles_OnlyContent.to_csv(r'datastore/AllArticles_OnlyContent.txt', header=None, index=None, sep=' ', mode='a')
AllArticles_HeadingPlusContent.to_csv(r'datastore/AllArticles_HeadingPlusContent.txt', header=None, index=None, sep=' ', mode='a')
Category.to_csv(r'target/category.txt', header=None, index=None, sep=' ', mode='a')<jupyter_output><empty_output><jupyter_text>Modelingทำการ Tunning hyperparameter และหา Model ที่ดีที่สุดในการทำนาย- K-Nearest Neighbors- XGBoost- Random forestโหลดข้อมูลที่เคย save เก็บไว้- `AllArticles_OnlyContent.txt` คือข้อมูลที่มีเฉพาะเนื้อหาข่าวโดยจะเก็บข้อมูลในตัวแปร raw_documents- `target/category.txt` คือส่วนของ target หรือเฉลยว่าข่าวดังกล่าวอยู่ในประเภทอะไร<jupyter_code>with open("datastore/AllArticles_OnlyContent.txt","r", encoding="utf8") as f: #ตรวจสอบจำนวนบรรทัด
    raw_documents = f.read().splitlines() #ใช้แยก string ที่จุดขึ้นบรรทัดใหม่คือ จุดที่มี '\n' แล้วคืนค่ากลับมาเป็นข้อมูลประเภท list
f.close() #ปิดไฟล์หลังใช้เสร็จ
print("Read %d raw text documents" % len(raw_documents)) #ตรวจสอบจำนวนบรรทัด
raw_documents[0] #เรียกดูข้อมูลตัวแรกที่อ่านมา
with open("target/category.txt","r", encoding="utf8") as f: #ตรวจสอบจำนวนบรรทัด
    y = f.read().splitlines() #ใช้แยก string ที่จุดขึ้นบรรทัดใหม่คือ จุดที่มี '\n' แล้วคืนค่ากลับมาเป็นข้อมูลประเภท list
f.close() #ปิดไฟล์หลังใช้เสร็จ
print("Read %d" % len(y)) #ตรวจสอบจำนวนบรรทัด<jupyter_output>Read 1408<jupyter_text>เช็คว่าข้อมูล imbalance ไหม<jupyter_code>#สร้าง dataframe จากข้อมูลที่ดึงเข้ามา
all_data = pd.DataFrame(list(zip(raw_documents, y)), 
               columns =['news', 'target'])
all_data
#นับจำนวนแต่ละ target แล้วเก็บไว้ที่ตัวแปร y_axis
y_axis = all_data['target'].value_counts()
y_axis
#เก็บ target ไว้ที่ตัวแปร x_axis
x_axis = all_data['target'].unique()
x_axis
#plot กราฟเพื่อดูว่าข้อมูล imbalance ไหม
plt.bar(x_axis, y_axis)<jupyter_output><empty_output><jupyter_text>ข้อมูลไม่ imbalance สามารถเอาไปใช้ได้ปกติ ไม่ต้องทำ upsampling หรือ downsampling  Text preprocessingสร้าง function ที่ต้องใช้ในการทำ preprocessing<jupyter_code># X_train, X_test, y_train, y_test = train_test_split(raw_documents, y, test_size=0.2, random_state=42) 
#แบ่งข้อมูลเป็น 2 ส่วน train 80% test 20%
# Tokenizer and WordNet Lemmatizer with POS (part of speech)
def lemma_tokenizer_w_pos_tag(text):

    def convert_tags(tag):
        #แปลง tag ที่เป็น vbd vbg vbz ให้เป็น v
        if tag == 'vbd' or tag == 'vbg' or tag == 'vbz':
            return 'v'
        #นอกเหนือจากเงื่อนไขช้างบนให้เป็น n
        else: 
            return 'n'

    standard_tokenizer = CountVectorizer().build_tokenizer() #สร้างตัวที่ใช้แยก text (string) ที่เข้ามาออกเป็น tokens
    tokens = standard_tokenizer(text) #แยก text (string) ที่เข้ามาออกเป็น tokens
    tokens_with_pos_tag = nltk.pos_tag(tokens) #ติด pos_tag ให้กับแต่ละ token

    lemmatizer = nltk.WordNetLemmatizer() #สร้างตัวที่ใช้แปลงรูปของคำให้อยู่รูปฟอร์มพื้นฐาน (รากคำ)
    lemma_tokens = [] #สร้าง list ชื่อ lemma_tokens เพื่อรอเก็บคำหลังจากแปลงด้วย pos_tag อันใหม่
    for token in tokens_with_pos_tag: #วน for loop ในตัวแปร tokens_with_pos_tag
        new_tag = convert_tags(token[1].lower())
        '''token[1] คือ pos_tag ที่เพิ่มเข้ามาเมื่อผ่านบรรทัดที่ 12 ( nltk.pos_tag(tokens) ) ใช้ .lower() เพื่อปรับให้เป็นตัวเล็ก
        แล้วจึงทำการส่งไปที่ function convert_tags() เพื่อแปลงให้เหลือแค่ 2 แบบคือถ้าเป็น 
        VBD verb: past tense took,
        VBG verb: gerund/present participle taking,
        VBZ verb: 3rd person sing. present takes
        จะแปลงให้เหลือแค่ v นอกเหนือจากนั้นจะแปลงเป็น n ทั้งหมด
        ซึ่ง pos_tag ที่ได้มาใหม่จะเก็บไว้ในตัวแปร new_tag'''
        
        lemma_tokens.append(lemmatizer.lemmatize(token[0], new_tag))
        '''จะนำ pos_tag ใหม่ที่ได้มาใช้กับ token[0] หรือคำที่ตอนบรรทัดที่ 12 เพื่อแปลงให้อยู่ในรูปของ pos_tag อันใหม่
        เช่น token[0] = amplifying, new_tag = v ผลลัพธ์ที่ได้คือ amplify แล้วจะเอาผลลัพธ์เพิ่มเข้าไปใน list ชื่อ lemma_tokens''' 
    
    return lemma_tokens<jupyter_output><empty_output><jupyter_text>ตัวอย่างเมื่อเอาคำเข้าไปใน function lemma_tokenizer_w_pos_tag()<jupyter_code>lemma_tokenizer_w_pos_tag('amplifying')
#ใช้ TfidfVectorizer() แทน CountVectorizer() และ TfidfTransformer()
#default ของ analyzer คือ word, กำจัด stop_words
non_weight = TfidfVectorizer(stop_words="english",min_df = 0,tokenizer=lemma_tokenizer_w_pos_tag)
#Term weighting ใน Scikit-learn เราสามารถสร้างที่ TF-IDF weighted document-term matrix โดยใช้ TfidfVectorizer() แทน CountVectorizer()
#default ของ analyzer คือ word, กำจัด stop_words
weight = TfidfVectorizer(analyzer='word', ngram_range=(1,3),
                     min_df = 0, stop_words = 'english', sublinear_tf=True, lowercase=True)<jupyter_output><empty_output><jupyter_text>--- สร้าง dict ชื่อ accuracy_dict เพื่อใช้เก็บค่า accuracy ของแต่ละ model<jupyter_code>accuracy_dict = dict() #สร้าง dict ชื่อ accuracy_dict เพื่อรอรับค่า accuracy<jupyter_output><empty_output><jupyter_text>ทำการ transform raw documents โดยจะแบ่งเป็น 2 แบบคือ non_weight และ weight- non_weight จะเก็บไว้ที่ตัวแปร non_weight_raw_documents- weight จะเก็บไว้ที่ตัวแปร weight_raw_documents<jupyter_code># transform raw documents เก็บไว้ที่ตัวแปร non_weight_raw_documents
non_weight_raw_documents = non_weight.fit_transform(raw_documents)
# transform raw documents เก็บไว้ที่ตัวแปร weight_raw_documents
weight_raw_documents = weight.fit_transform(raw_documents)<jupyter_output><empty_output><jupyter_text>GridSearchCV Functionสร้าง Function ที่ใช้ในการทำ hyperparameter tuning<jupyter_code>def tuning_params(model, params): #รับค่าเป็น model และ params ที่จะทำการ tune
    folds = 3 #กำหนด fold ที่จะใช้ในการทำ K-fold
    skf = StratifiedKFold(n_splits=folds, shuffle = True, random_state = 1001) #ทำการกำหนดเครื่องมือ K-fold ว่าจะใช้ 3 folds โดย StratifiedKFold เป็นการแบ่งข้อมูลรูปแบบหนึ่ง
    #เอา Classifier, params ใส่ลงไปและ กำหนด cv ด้วย k-flod ที่เรากำหนดไว้ในตัวแปร skf
    grid = GridSearchCV(model, params, verbose = 3, cv=skf.split(X_train,y_train), n_jobs = -1)
    grid.fit(X_train, y_train) #เอา GridSearchCV มา fit กับ train set
    return(grid.best_params_) #คืนค่า hyperparameter ที่ดีที่สุดกลับไป<jupyter_output><empty_output><jupyter_text>K-Nearest Neighborsในการทำ Tunning parameter จะต้องกำหนด parameter ที่เราจะทำการวิเคราะห์เช่น knn จะต้องมีการกำหนดจำนวน k<jupyter_code>#ทำการกำหนด parameter ที่จะเอาไปวิเคราะห์ โดยค่าที่กำหนดไว้คือ parameter ของ knn
params = { 
    'n_neighbors' : list(np.arange(1,50,2)), #ให้ทำการทดลองหา k ที่ดีที่สุดตั้ง 1-50 โดยขยับทีละ 2 เช่น 1, 3, 5, ...
    'weights' : ['uniform', 'distance'], #weight function ที่ใช้ในการทำนาย
    'metric' : ['euclidean', 'manhattan'] #คิดระยะหางด้วยวิธีไหน
}<jupyter_output><empty_output><jupyter_text>KNN Tunning parameter แบบไม่ weightทำการแบ่ง data ออกเป็น 2 ส่วนคือ **train set** (X_train, y_train) กับ **test set** (X_test, y_test) โดยส่วน train set จะใช้ในการทำ Tunning parameter, Model selection และ Train model โดยการทำ Tunning parameter จะใช้ **GridSearchCV**, การทำ Model selection จะใช้ **cross_val_score** โดยเลือก model ที่มีค่า accuracy สูงที่สุด<jupyter_code>X_train, X_test, y_train, y_test = train_test_split(non_weight_raw_documents, y, test_size=0.2, random_state=42)
#แบ่งข้อมูลเป็น 2 ส่วน train 80% test 20%<jupyter_output><empty_output><jupyter_text>เอาค่าทั้งหมดที่เรากำหนดไว้มาใส่ GridSearchCV<jupyter_code>knn_non_weight=KNeighborsClassifier() #เรียกใช้ KNN
knn_non_weight_best_params = tuning_params(knn_non_weight, params) #ทำ hyperparameter tuning
knn_non_weight_best_params #แสดงผลที่ได้กลับมา<jupyter_output>Fitting 3 folds for each of 100 candidates, totalling 300 fits<jupyter_text>KNN แบบไม่ weight Best paramsนำ best parameter ที่หาได้มาใส่ที่ classifier ทำการ fit model กับ parameter ที่ดีที่สุดที่วิเคราะห์มาได้<jupyter_code># กำหนด parameter ตามที่หาได้
knn_non_weight.set_params(**knn_non_weight_best_params) #ทำการ set hyperparameter ที่ได้จากการทำ gridsearchcv
knn_non_weight.fit(X_train, y_train) #ทำการ fit model<jupyter_output><empty_output><jupyter_text>หา accuracy ด้วย cross_val_score<jupyter_code>#cross_val_score จะ return accuracy ออกมาเป็น list โดยจำนวน accuracy ที่ return กลับมาจะเท่ากับ cv ที่เรากำหนดไว้
scores = cross_val_score(knn_non_weight, X_train, y_train, scoring="accuracy", cv=10)
print("knn_non_weight model: ",scores.mean()) #เอา list ของ accuracy มาหา mean
#เพิ่ม accuracy ที่เฉลี่ยกันแล้วไปใน accuracy_dict ที่สร้างรอไว้ โดยให้ key เป็นชื่อ model และ value เป็น accuracy
accuracy_dict['knn_non_weight'] = scores.mean()<jupyter_output><empty_output><jupyter_text>Test KNN แบบไม่ weightเอา model ที่ใช้ parameter ที่ดีที่สุดมา test กับ test set ที่แบ่งไว้ตอนแรก<jupyter_code>#นำ X_test มาทำนาย
predicted = knn_non_weight.predict(X_test)
# หา accuracy โดยเอา เฉลยของ X_test มาเทียบกับ predicted หรือค่าที่ model เราทำนาย
accuracy_score(y_test, predicted)<jupyter_output><empty_output><jupyter_text>KNN Tunning parameter แบบ weight<jupyter_code>X_train, X_test, y_train, y_test = train_test_split(weight_raw_documents, y, test_size=0.2, random_state=42)
#แบ่งข้อมูลเป็น 2 ส่วน train 80% test 20%<jupyter_output><empty_output><jupyter_text>เอาค่าทั้งหมดที่เรากำหนดไว้มาใส่ GridSearchCV<jupyter_code>knn_weight=KNeighborsClassifier() #เรียกใช้ KNN
knn_weight_best_params = tuning_params(knn_weight, params) #ทำ hyperparameter tuning 
knn_weight_best_params #แสดงผลที่ได้กลับมา<jupyter_output>Fitting 3 folds for each of 100 candidates, totalling 300 fits<jupyter_text>KNN แบบweight Best paramsนำ best parameter ที่หาได้มาใส่ที่ classifier ทำการ fit model กับ parameter ที่ดีที่สุดที่วิเคราะห์มาได้<jupyter_code># กำหนด parameter ตามที่หาได้
knn_weight.set_params(**knn_weight_best_params) #ทำการ set hyperparameter ที่ได้จากการทำ gridsearchcv
knn_weight.fit(X_train, y_train) #ทำการ fit model<jupyter_output><empty_output><jupyter_text>หา accuracy ด้วย cross_val_score<jupyter_code>#cross_val_score จะ return accuracy ออกมาเป็น list โดยจำนวน accuracy ที่ return กลับมาจะเท่ากับ cv ที่เรากำหนดไว้
scores = cross_val_score(knn_weight, X_train, y_train, scoring="accuracy", cv=10)
print("knn_weight model: ",scores.mean()) #เอา list ของ accuracy มาหา mean
#เพิ่ม accuracy ที่เฉลี่ยกันแล้วไปใน accuracy_dict ที่สร้างรอไว้ โดยให้ key เป็นชื่อ model และ value เป็น accuracy
accuracy_dict['knn_weight'] = scores.mean()<jupyter_output><empty_output><jupyter_text>Test KNN แบบ weightเอา model ที่ใช้ parameter ที่ดีที่สุดมา test กับ test set ที่แบ่งไว้ตอนแรก<jupyter_code>#นำ X_test มาทำนาย
predicted = knn_weight.predict(X_test)
# หา accuracy โดยเอา เฉลยของ X_test มาเทียบกับ predicted หรือค่าที่ model เราทำนาย
accuracy_score(y_test, predicted)<jupyter_output><empty_output><jupyter_text>---  XGBoost<jupyter_code>#ค่าที่กำหนดไว้คือ parameter ของ XGBoost
params = {
        'min_child_weight': [1, 5],
        'gamma': [0.5, 1, 1.5],
        'subsample': [0.6, 0.8, 1.0],
        'colsample_bytree': [0.6, 0.8, 1.0],
        'max_depth': [3, 4, 5]
        }<jupyter_output><empty_output><jupyter_text>XGBoost Tunning parameter แบบไม่ weight<jupyter_code>X_train, X_test, y_train, y_test = train_test_split(non_weight_raw_documents, y, test_size=0.2, random_state=42)
#แบ่งข้อมูลเป็น 2 ส่วน train 80% test 20%<jupyter_output><empty_output><jupyter_text>เอาค่าทั้งหมดที่เรากำหนดไว้มาใส่ GridSearchCV<jupyter_code>import xgboost as xgb
xgb_non_weight = xgb.XGBClassifier() #เรียกใช้ XGBoost
xgb_non_weight_best_params = tuning_params(xgb_non_weight, params) #ทำ hyperparameter tuning 
xgb_non_weight_best_params #แสดงผลที่ได้กลับมา<jupyter_output>Fitting 3 folds for each of 162 candidates, totalling 486 fits<jupyter_text>XGBoost แบบไม่ weight Best paramsนำ best parameter ที่หาได้มาใส่ที่ classifier ทำการ fit model กับ parameter ที่ดีที่สุดที่วิเคราะห์มาได้<jupyter_code># กำหนด parameter ตามที่หาได้
xgb_non_weight.set_params(**xgb_non_weight_best_params) #ทำการ set hyperparameter ที่ได้จากการทำ gridsearchcv
xgb_non_weight.fit(X_train, y_train) #ทำการ fit model<jupyter_output><empty_output><jupyter_text>หา accuracy ด้วย cross_val_score<jupyter_code>#cross_val_score จะ return accuracy ออกมาเป็น list โดยจำนวน accuracy ที่ return กลับมาจะเท่ากับ cv ที่เรากำหนดไว้
scores = cross_val_score(xgb_non_weight, X_train, y_train, scoring="accuracy", cv=10)
print("xgb_non_weight model: ",scores.mean()) #เอา list ของ accuracy มาหา mean
#เพิ่ม accuracy ที่เฉลี่ยกันแล้วไปใน accuracy_dict ที่สร้างรอไว้ โดยให้ key เป็นชื่อ model และ value เป็น accuracy
accuracy_dict['xgb_non_weight'] = scores.mean()<jupyter_output><empty_output><jupyter_text>Test XGBoost แบบไม่ weightเอา model ที่ใช้ parameter ที่ดีที่สุดมา test กับ test set ที่แบ่งไว้ตอนแรก<jupyter_code>#นำ X_test มาทำนาย
predicted = xgb_non_weight.predict(X_test)
# หา accuracy โดยเอา เฉลยของ X_test มาเทียบกับ predicted หรือค่าที่ model เราทำนาย
accuracy_score(y_test, predicted)<jupyter_output><empty_output><jupyter_text>XGBoost Tunning parameter แบบ weight<jupyter_code>X_train, X_test, y_train, y_test = train_test_split(weight_raw_documents, y, test_size=0.2, random_state=42)
#แบ่งข้อมูลเป็น 2 ส่วน train 80% test 20%<jupyter_output><empty_output><jupyter_text>เอาค่าทั้งหมดที่เรากำหนดไว้มาใส่ GridSearchCV<jupyter_code>import xgboost as xgb
xgb_weight = xgb.XGBClassifier() #เรียกใช้ XGBoost
xgb_weight_best_params = tuning_params(xgb_weight, params) #ทำ hyperparameter tuning 
xgb_weight_best_params #แสดงผลที่ได้กลับมา<jupyter_output>Fitting 3 folds for each of 162 candidates, totalling 486 fits<jupyter_text>XGBoost แบบ weight Best paramsนำ best parameter ที่หาได้มาใส่ที่ classifier ทำการ fit model กับ parameter ที่ดีที่สุดที่วิเคราะห์มาได้<jupyter_code># กำหนด parameter ตามที่หาได้
xgb_weight.set_params(**xgb_weight_best_params) #ทำการ set hyperparameter ที่ได้จากการทำ gridsearchcv
xgb_weight.fit(X_train, y_train) #ทำการ fit model<jupyter_output><empty_output><jupyter_text>หา accuracy ด้วย cross_val_score<jupyter_code>#cross_val_score จะ return accuracy ออกมาเป็น list โดยจำนวน accuracy ที่ return กลับมาจะเท่ากับ cv ที่เรากำหนดไว้
scores = cross_val_score(xgb_weight, X_train, y_train, scoring="accuracy", cv=10)
print("xgb_weight model: ",scores.mean()) #เอา list ของ accuracy มาหา mean
#เพิ่ม accuracy ที่เฉลี่ยกันแล้วไปใน accuracy_dict ที่สร้างรอไว้ โดยให้ key เป็นชื่อ model และ value เป็น accuracy
accuracy_dict['xgb_weight'] = scores.mean()<jupyter_output><empty_output><jupyter_text>Test XGBoost แบบ weightเอา model ที่ใช้ parameter ที่ดีที่สุดมา test กับ test set ที่แบ่งไว้ตอนแรก<jupyter_code>#นำ X_test มาทำนาย
predicted = xgb_weight.predict(X_test)
# หา accuracy โดยเอา เฉลยของ X_test มาเทียบกับ predicted หรือค่าที่ model เราทำนาย
accuracy_score(y_test, predicted)<jupyter_output><empty_output><jupyter_text>---  Random forest<jupyter_code>#ค่าที่กำหนดไว้คือ parameter ของ Random forest
params = { 
    'n_estimators': [200, 500],
    'max_features': ['auto', 'sqrt', 'log2'],
    'max_depth' : [4,5,6,7,8],
    'criterion' :['gini', 'entropy']
}<jupyter_output><empty_output><jupyter_text>Random forest Tunning parameter แบบไม่ weight<jupyter_code>X_train, X_test, y_train, y_test = train_test_split(non_weight_raw_documents, y, test_size=0.2, random_state=42)
#แบ่งข้อมูลเป็น 2 ส่วน train 80% test 20%<jupyter_output><empty_output><jupyter_text>เอาค่าทั้งหมดที่เรากำหนดไว้มาใส่ GridSearchCV<jupyter_code>from sklearn.ensemble import RandomForestClassifier
rf_non_weight = RandomForestClassifier() #เรียกใช้ Random Forest
rf_non_weight_best_params = tuning_params(rf_non_weight, params) #ทำ hyperparameter tuning 
rf_non_weight_best_params #แสดงผลที่ได้กลับมา<jupyter_output>Fitting 3 folds for each of 60 candidates, totalling 180 fits<jupyter_text>Random forest แบบไม่ weight Best paramsนำ best parameter ที่หาได้มาใส่ที่ classifier ทำการ fit model กับ parameter ที่ดีที่สุดที่วิเคราะห์มาได้<jupyter_code># กำหนด parameter ตามที่หาได้
rf_non_weight.set_params(**rf_non_weight_best_params) #ทำการ set hyperparameter ที่ได้จากการทำ gridsearchcv
rf_non_weight.fit(X_train, y_train) #ทำการ fit model<jupyter_output><empty_output><jupyter_text>หา accuracy ด้วย cross_val_score<jupyter_code>#cross_val_score จะ return accuracy ออกมาเป็น list โดยจำนวน accuracy ที่ return กลับมาจะเท่ากับ cv ที่เรากำหนดไว้
scores = cross_val_score(rf_non_weight, X_train, y_train, scoring="accuracy", cv=10)
print("rf_non_weight model: ",scores.mean()) #เอา list ของ accuracy มาหา mean
#เพิ่ม accuracy ที่เฉลี่ยกันแล้วไปใน accuracy_dict ที่สร้างรอไว้ โดยให้ key เป็นชื่อ model และ value เป็น accuracy
accuracy_dict['rf_non_weight'] = scores.mean()<jupyter_output><empty_output><jupyter_text>Test Random forest แบบไม่ weightเอา model ที่ใช้ parameter ที่ดีที่สุดมา test กับ test set ที่แบ่งไว้ตอนแรก<jupyter_code>#นำ X_test มาทำนาย
predicted = rf_non_weight.predict(X_test)
# หา accuracy โดยเอา เฉลยของ X_test มาเทียบกับ predicted หรือค่าที่ model เราทำนาย
accuracy_score(y_test, predicted)<jupyter_output><empty_output><jupyter_text>Random forest Tunning parameter แบบ weight<jupyter_code>X_train, X_test, y_train, y_test = train_test_split(weight_raw_documents, y, test_size=0.2, random_state=42)
#แบ่งข้อมูลเป็น 2 ส่วน train 80% test 20%<jupyter_output><empty_output><jupyter_text>เอาค่าทั้งหมดที่เรากำหนดไว้มาใส่ GridSearchCV<jupyter_code>from sklearn.ensemble import RandomForestClassifier
rf_weight = RandomForestClassifier() #เรียกใช้ Random Forest
rf_weight_best_params = tuning_params(rf_weight, params) #ทำ hyperparameter tuning 
rf_weight_best_params #แสดงผลที่ได้กลับมา<jupyter_output>[Parallel(n_jobs=-1)]: Using backend LokyBackend with 12 concurrent workers.<jupyter_text>Random forest แบบ weight Best paramsนำ best parameter ที่หาได้มาใส่ที่ classifier ทำการ fit model กับ parameter ที่ดีที่สุดที่วิเคราะห์มาได้<jupyter_code># กำหนด parameter ตามที่หาได้
rf_weight.set_params(**rf_weight_best_params) #ทำการ set hyperparameter ที่ได้จากการทำ gridsearchcv
rf_weight.fit(X_train, y_train) #ทำการ fit model<jupyter_output><empty_output><jupyter_text>หา accuracy ด้วย cross_val_score<jupyter_code>#cross_val_score จะ return accuracy ออกมาเป็น list โดยจำนวน accuracy ที่ return กลับมาจะเท่ากับ cv ที่เรากำหนดไว้
scores = cross_val_score(rf_weight, X_train, y_train, scoring="accuracy", cv=10)
print("rf_weight model: ",scores.mean()) #เอา list ของ accuracy มาหา mean
#เพิ่ม accuracy ที่เฉลี่ยกันแล้วไปใน accuracy_dict ที่สร้างรอไว้ โดยให้ key เป็นชื่อ model และ value เป็น accuracy
accuracy_dict['rf_weight'] = scores.mean()<jupyter_output><empty_output><jupyter_text>Test Random forest แบบ weightเอา model ที่ใช้ parameter ที่ดีที่สุดมา test กับ test set ที่แบ่งไว้ตอนแรก<jupyter_code>#นำ X_test มาทำนาย
predicted = xgb_weight.predict(X_test)
# หา accuracy โดยเอา เฉลยของ X_test มาเทียบกับ predicted หรือค่าที่ model เราทำนาย
accuracy_score(y_test, predicted)<jupyter_output><empty_output><jupyter_text>---  Conclusionหลังจากทำการทดสอบ model ทุกแบบแล้ว จึงนำ accuracy ที่ได้จาก cross_val_score มาเปรียบเทียบกัน<jupyter_code>accuracy_dict
# นำ accuracy_dict มาแปลงเป็น DataFrame เพื่อให้ง่ายต่อการอ่านและเปรียบเทียบ
accuracy_df = pd.DataFrame(list(accuracy_dict.items()),columns = ['Model','Accuracy'])
accuracy_df
#ทำการเรียงลำดับตามค่า Accuracy โดยเรียงจากมากไปน้อย
accuracy_df = accuracy_df.sort_values(by=['Accuracy'], ascending=False)
#ทำการ plot bar chart เพื่อให้ง่ายต่อการอ่านค่าและเปรียบเทียบ โดยกำหนดให้ take log เพื่อใช้ในการแสดงค่า
plt.rcParams["figure.figsize"] = (13,5)
plt.bar(accuracy_df['Model'], accuracy_df['Accuracy'].round(4), log=True)
plt.ylabel('Accuracy')
plt.xlabel('Model');<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.14072187097170216, 'fraction_numerical': 0.008317475906942484, 'mean_word_length': 7.939094650205761, 'pattern_counts': {'":': 0, '<': 229, '<?xml version=': 0, '>': 262, 'https://': 0, 'lorem ipsum': 0, 'www.': 9, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '59', 'dir': 'jupyter-structured-clean-dedup', 'id': '428905', 'license': 'MIT', 'n_tokens_mistral': 17425, 'n_tokens_neox': 18029, 'n_words': 2325, 'path': 'News/.ipynb_checkpoints/news-checkpoint.ipynb', 'repo_name': 'kittipon-shine/News_Quote_NLP', 'size': '32582'}