!pip install pythainlp
!wget http://www.donlapark.cmustat.com/229352/thai_lyrics.csv
Looking in indexes: https://pypi.org/simple, https://us-python.pkg.dev/colab-wheels/public/simple/ Collecting pythainlp Downloading pythainlp-3.1.0-py3-none-any.whl (9.6 MB) |████████████████████████████████| 9.6 MB 29.8 MB/s Requirement already satisfied: requests>=2.22.0 in /usr/local/lib/python3.7/dist-packages (from pythainlp) (2.23.0) Requirement already satisfied: idna<3,>=2.5 in /usr/local/lib/python3.7/dist-packages (from requests>=2.22.0->pythainlp) (2.10) Requirement already satisfied: urllib3!=1.25.0,!=1.25.1,<1.26,>=1.21.1 in /usr/local/lib/python3.7/dist-packages (from requests>=2.22.0->pythainlp) (1.24.3) Requirement already satisfied: certifi>=2017.4.17 in /usr/local/lib/python3.7/dist-packages (from requests>=2.22.0->pythainlp) (2022.6.15) Requirement already satisfied: chardet<4,>=3.0.2 in /usr/local/lib/python3.7/dist-packages (from requests>=2.22.0->pythainlp) (3.0.4) Installing collected packages: pythainlp Successfully installed pythainlp-3.1.0 --2022-09-27 03:17:43-- http://www.donlapark.cmustat.com/229352/thai_lyrics.csv Resolving www.donlapark.cmustat.com (www.donlapark.cmustat.com)... 150.107.31.67 Connecting to www.donlapark.cmustat.com (www.donlapark.cmustat.com)|150.107.31.67|:80... connected. HTTP request sent, awaiting response... 200 OK Length: 44871532 (43M) [text/csv] Saving to: ‘thai_lyrics.csv’ thai_lyrics.csv 100%[===================>] 42.79M 3.32MB/s in 17s 2022-09-27 03:18:01 (2.45 MB/s) - ‘thai_lyrics.csv’ saved [44871532/44871532]
import pandas as pd
from itertools import chain
from pythainlp import word_tokenize
from collections import Counter
import matplotlib.pyplot as plt
import seaborn as sns
import csv
import numpy as np
df = pd.read_csv('thai_lyrics.csv', engine='python')
df.head()
url | soup | title | artist_name | full_lyrics | lyrics | |
---|---|---|---|---|---|---|
0 | https://www.siamzone.com/music/thailyric/14050 | <!DOCTYPE HTML>\n\n<html lang="th">\n<head>\n<... | เนื้อเพลง แบบว่าหวั่นไหว (Ost. เด็ดปีกนางฟ้า) | กลม อรวี พินิจสารภิรมย์ | ก็เธอน่ะทำให้ใจมันเต้นแรง แรง\nแต่เขาก็ทำให้ใจ... | ก็เธอน่ะทำให้ใจมันเต้นแรง แรง\nแต่เขาก็ทำให้ใจ... |
1 | https://www.siamzone.com/music/thailyric/14051 | <!DOCTYPE HTML>\n\n<html lang="th">\n<head>\n<... | เนื้อเพลง ที่ผ่านมา | เยิ้ม Yerm | วันแห่งความรัก ขอมอบเพลงนี้ให้ใครสักคน\nที่ยัง... | วันแห่งความรัก ขอมอบเพลงนี้ให้ใครสักคน\nที่ยัง... |
2 | https://www.siamzone.com/music/thailyric/14052 | <!DOCTYPE HTML>\n\n<html lang="th">\n<head>\n<... | เนื้อเพลง ในตอนนั้น | โอม รัธพงศ์ ภูรีสิทธิ์ Youngohm | เมื่อก่อนนี้มีแค่เรา\nในความคิดเป็นแค่เงา\nไม่... | เมื่อก่อนนี้มีแค่เรา\nในความคิดเป็นแค่เงา\nไม่... |
3 | https://www.siamzone.com/music/thailyric/14053 | <!DOCTYPE HTML>\n\n<html lang="th">\n<head>\n<... | เนื้อเพลง อยู่ในใจ | ปู พงษ์สิทธิ์ คำภีร์ | ขอบคุณวันเวลา ที่ผ่านมา ขอบคุณสิ่งดีที่เธอให้\... | ขอบคุณวันเวลา ที่ผ่านมา ขอบคุณสิ่งดีที่เธอให้\... |
4 | https://www.siamzone.com/music/thailyric/14054 | <!DOCTYPE HTML>\n\n<html lang="th">\n<head>\n<... | เนื้อเพลง ชู้กะชู้ | เอ๊ะ พงศ์จักร พิษฐานพร Aeh Syndrome | เอ๊ะ ขาวใสๆ ช่างน่ามอง\nเอ๊ะ รอยยิ้มของเธอ ทำฉ... | เอ๊ะ ขาวใสๆ ช่างน่ามอง\nเอ๊ะ รอยยิ้มของเธอ ทำฉ... |
tokenized_lyrics = df['lyrics'].map(word_tokenize)
print(tokenized_lyrics[0])
['ก็', 'เธอ', 'น่ะ', 'ทำให้', 'ใจ', 'มัน', 'เต้น', 'แรง', ' ', 'แรง', '\n', 'แต่', 'เขา', 'ก็', 'ทำให้', 'ใจ', 'อยาก', 'กรี๊ด', 'รัว', ' ', 'รัว', '\n', 'ผลัดกัน', 'มา', 'ทำคะแนน', 'จน', 'ฉัน', 'กลัว', ' ', 'กลัว', ' ', 'หัวใจ', 'วุ่นวาย', '\n', '\n', 'ก็', 'เธอ', 'น่ะ', 'คอย', 'เอาใจ', 'แบบ', 'โอ๊ย', 'คือ', 'ดี', '\n', 'แต่', 'เค้า', 'ก็', 'มี', 'สไตล์', 'แบบ', 'โอ๊ย', 'คือ', 'โดน', '\n', 'จิตใจ', 'เลย', 'มี', 'อาการ', 'แบบ', 'เริ่ม', 'ซน', ' ', 'ซน', ' ', 'นิดหนึ่ง', '\n', '\n', 'แบบ', 'ว่า', ' ', 'ว้า', ' ', 'ว้า', ' ', 'ว้า', ' ', 'ว้า', ' ', 'ว้า', ' ', 'ว้า', ' ', 'วา', ' ', 'หวั่นไหว', '\n', 'แบบ', 'ว่า', 'น่ารัก', ' ', 'น่ารัก', ' ', 'น่ารัก', ' ', 'ทั้งคู่', 'เลย', ' ', 'มัน', 'ยิ่ง', 'คิด', 'ยิ่ง', 'คิดไม่ออก', '\n', 'กูเกิ้ล', 'ก็', 'ไม่', 'บอก', ' ', 'เธอ', 'น่า', 'เก็บ', 'ไว้', 'ทั้งสอง', 'คน', '\n', 'แบบ', 'มัน', 'นะ', ' ', 'นะ', ' ', 'นะ', ' ', 'นะ', ' ', 'นะ', ' ', 'นะ', ' ', 'นอ', 'ยด์', 'กว่า', 'ใคร', '\n', 'จะ', 'ให้', 'เลือก', 'ก็', 'ไม่', 'ไหว', ' ', 'โจทย์', 'มัน', 'ยาก', 'เกิน', 'ทน', '\n', 'มัน', 'เลือกไม่ได้', 'สักที', ' ', 'เลย', 'เลือกไม่ได้', 'สักที', ' ', 'ทั้งสอง', 'คน', '\n', '\n', 'อย่า', 'หาว่า', 'ฉัน', 'เป็น', 'คนเจ้าชู้', 'บี', 'ดู', '\n', 'ไม่', 'ได้', 'ตั้งใจ', 'อย่า', 'เพิ่ง', 'ใส่ร้าย', 'เลย', 'ยู', '\n', 'ไม่', 'เคย', 'จะ', 'เป็น', 'อย่างนี้', 'มา', 'มะ', 'มา', 'ดู', ' ', 'หน้า', 'คน', 'กลุ้มใจ', '\n', '\n', 'ก็', 'เธอ', 'น่ะ', 'คอย', 'เอาใจ', 'แบบ', 'โอ๊ย', 'คือ', 'ดี', '\n', 'แต่', 'เค้า', 'ก็', 'มี', 'สไตล์', 'แบบ', 'โอ๊ย', 'คือ', 'โดน', '\n', 'จิตใจ', 'เลย', 'มี', 'อาการ', 'แบบ', 'เริ่ม', 'ซน', ' ', 'ซน', ' ', 'นิดหนึ่ง', '\n', '\n', 'แบบ', 'ว่า', ' ', 'ว้า', ' ', 'ว้า', ' ', 'ว้า', ' ', 'ว้า', ' ', 'ว้า', ' ', 'ว้า', ' ', 'วา', ' ', 'หวั่นไหว', '\n', 'แบบ', 'ว่า', 'น่ารัก', ' ', 'น่ารัก', ' ', 'น่ารัก', ' ', 'ทั้งคู่', 'เลย', ' ', 'มัน', 'ยิ่ง', 'คิด', 'ยิ่ง', 'คิดไม่ออก', '\n', 'กูเกิ้ล', 'ก็', 'ไม่', 'บอก', ' ', 'เธอ', 'น่า', 'เก็บ', 'ไว้', 'ทั้งสอง', 'คน', '\n', 'แบบ', 'มัน', 'นะ', ' ', 'นะ', ' ', 'นะ', ' ', 'นะ', ' ', 'นะ', ' ', 'นะ', ' ', 'นอ', 'ยด์', 'กว่า', 'ใคร', '\n', 'จะ', 'ให้', 'เลือก', 'ก็', 'ไม่', 'ไหว', ' ', 'โจทย์', 'มัน', 'ยาก', 'เกิน', 'ทน', '\n', 'มัน', 'เลือกไม่ได้', 'สักที', ' ', 'เลย', 'เลือกไม่ได้', 'สักที', ' ', 'ทั้งสอง', 'คน', '\n', '\n', 'แบบ', 'ว่า', ' ', 'ว้า', ' ', 'ว้า', ' ', 'ว้า', ' ', 'ว้า', ' ', 'ว้า', ' ', 'ว้า', ' ', 'วา', ' ', 'หวั่นไหว', '\n', 'แบบ', 'ว่า', 'น่ารัก', ' ', 'น่ารัก', ' ', 'น่ารัก', ' ', 'ทั้งคู่', 'เลย', ' ', 'มัน', 'ยิ่ง', 'คิด', 'ยิ่ง', 'คิดไม่ออก', '\n', 'กูเกิ้ล', 'ก็', 'ไม่', 'บอก', ' ', 'เธอ', 'น่า', 'เก็บ', 'ไว้', 'ทั้งสอง', 'คน', '\n', 'แบบ', 'มัน', 'นะ', ' ', 'นะ', ' ', 'นะ', ' ', 'นะ', ' ', 'นะ', ' ', 'นะ', ' ', 'นอ', 'ยด์', 'กว่า', 'ใคร', '\n', 'จะ', 'ให้', 'เลือก', 'ก็', 'ไม่', 'ไหว', ' ', 'โจทย์', 'มัน', 'ยาก', 'เกิน', 'ทน', '\n', 'อย่า', 'เพิ่ง', 'หนี', 'และ', 'อย่า', 'เพิ่ง', 'บ่น', ' ', 'ถ้า', 'รัก', 'ฉัน', 'แล้ว', 'ต้อง', 'ทน', ' ', 'นะ', 'ทั้งสอง', 'คน']
#[[song , number , one],[song , number , two]] -> [song , number , one , song , number , two]
def flatten(ls):
"""
Flatten list of list
"""
return list(chain.from_iterable(ls))
#[song , number ,one, number, two] -> [1,2,3,2,4] and [1,2,3] -> [song , number , one]
def create_lookup_dict(tokenized_lyrics, n_min=None):
"""
Create lookup dictionary from list of words (lyrics)
"""
word_counts = Counter(tokenized_lyrics)
sorted_vocab = sorted(word_counts, key=word_counts.get, reverse=True)
if n_min is not None:
sorted_vocab = {k: v for k, v in word_counts.items() if v >= n_min}
vocab_to_int = {word: i for i, word in enumerate(sorted_vocab, 0)}
int_to_vocab = {i: word for word, i in vocab_to_int.items()}
return (vocab_to_int, int_to_vocab)
tokenized_lyrics = flatten(tokenized_lyrics)
tokenized_lyrics = [token if token is not '\n' else ' ' for token in tokenized_lyrics]
word_counts = Counter(tokenized_lyrics)
vocab_to_int, int_to_vocab = create_lookup_dict(tokenized_lyrics, n_min=None)
vocab_to_int["ใคร"]
28
len(vocab_to_int)
10035
int_to_vocab[12]
'ว่า'
sequence_length = 20
tokenized_indices = [vocab_to_int.get(token, 0) for token in tokenized_lyrics]
X, target = [], []
for n in range(0, len(tokenized_indices) - sequence_length, 1):
x = tokenized_indices[n: n + sequence_length]
y = tokenized_indices[n + sequence_length]
X.append(np.array(x))
target.append(y)
X = np.array(X)
target = np.array(target)
X[0]
array([ 7, 1, 292, 65, 22, 10, 501, 361, 0, 361, 0, 17, 32, 7, 65, 22, 25, 4481, 1487, 0])
target[0]
1487
import torch
import torch.nn as nn
import torch.optim as optim
from torch.utils.data import DataLoader
class MyDataSet(torch.utils.data.Dataset):
def __init__(self, X, y):
super(MyDataSet, self).__init__()
self._X = X
self._y = y
def __len__(self):
return self._X.shape[0]
def __getitem__(self, index):
X = self._X[index]
y = self._y[index]
return X, y
# Hyperparameters
LEARNING_RATE = 0.001
BATCH_SIZE = 128
NUM_EPOCHS = 15
# Classification
NUM_CLASSES = 10035
dataset = MyDataSet(X, target)
trainloader = DataLoader(dataset, batch_size=BATCH_SIZE, shuffle=True)
nn.Embedding(num_vocabs, hidden_dim)
embedding = nn.Embedding(10, 3)
# a batch of 2 samples of 4 indices each
input = torch.LongTensor([[1,2,4,5],[4,3,2,9]])
output = embedding(input)
output.shape
torch.Size([2, 4, 3])
lstm = nn.LSTM(10, 20, 2)
input = torch.randn(5, 3, 10)
h0 = torch.randn(2, 3, 20)
c0 = torch.randn(2, 3, 20)
output, (h1, c1) = lstm(input, (h0, c0))
output.shape
torch.Size([5, 3, 20])
from typing_extensions import Self
class Simple_LSTM(nn.Module):
def __init__(self):
super(Simple_LSTM, self).__init__()
# TODO 1: Fill in the layers' parameters, with all hidden dimensions = 128
self.embeddings = nn.Embedding(, )
self.lstm = nn.LSTM(, , dropout = 0.2, num_layers = 2)
self.fc = nn.Linear(, )
def forward(self, x):
# for LSTM, input should be (Sequnce_length,batchsize,hidden_layer),
# so we need to transpose the input
x = x.t()
# TODO 2: Apply the Embedding layer
x = self.embeddings(x)
# TODO 3: Apply the LSTM layer (note: LSTM's output is a tuple!)
h, _ = self.lstm(x)
# Only need to keep the last element of the sequence
ht=h[-1]
out = self.fc(ht)
return out
model = Simple_LSTM().to('cuda')
loss_fn = nn.CrossEntropyLoss()
optimizer = torch.optim.Adam(model.parameters(), lr=LEARNING_RATE)
def train_loop(dataloader, model, loss_fn, optimizer):
size = len(dataloader.dataset)
for batch, (X, y) in enumerate(dataloader):
# Compute prediction and loss
X = X.to('cuda')
y = y.to('cuda')
pred = model(X)
loss = loss_fn(pred, y)
# Backpropagation
optimizer.zero_grad()
loss.backward()
optimizer.step()
if batch % 1000 == 0:
loss, current = loss.item(), batch * len(X)
print(f"loss: {loss:>7f} [{current:>5d}/{size:>5d}]")
def generate(model, start_word, int_to_vocab, pad_value=0, predict_len=100):
words = word_tokenize(start_word)
start_word_ids = []
predicted = words
word_ids = [vocab_to_int.get(word, pad_value) for word in words]
#[28,15,16] -> [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,28,15,16]
current_seq = [np.pad(word_ids, (20 - len(word_ids), pad_value), 'constant')]
for _ in range(predict_len):
current_seq = torch.LongTensor(current_seq).to('cuda')
# get the next word probabilities
p = model(current_seq)
p = nn.Softmax(dim=1)(p).cpu().detach().numpy()
# p = [[0.1,0.2,0.05,0.03,0.02,0.3,0.2,0.1]]
p = p[0]
# p = [0.1,0.2,0.05,0.03,0.02,0.3,0.2,0.1]
# Sample from probability distribution p
word_i = np.random.choice(np.arange(0,p.shape[0]),p=p)
#word_i is an integer representing a word.
#### TODO: Fill in the following two lines of code#########
# Convert from word_i (int)--> word (str)
# and append the word from 1. into `predicted` list.
##################end#####################################
# the generated word becomes the next "current sequence" and the cycle can continue
current_seq = current_seq.cpu().detach().numpy()
current_seq = np.roll(current_seq, -1, 1)
current_seq[-1][-1] = word_i
gen_sentences = ''.join(predicted)
return gen_sentences
generate
function to generate three more songs. You may try using different starting words.¶for t in range(NUM_EPOCHS):
print(f"Epoch {t+1}\n-------------------------------")
train_loop(trainloader, model, loss_fn, optimizer)
print(generate(model, 'ใคร', int_to_vocab, predict_len=100))
print("Done!")
Epoch 1 ------------------------------- loss: 9.203841 [ 0/370475] loss: 5.516397 [128000/370475] loss: 5.002436 [256000/370475]
--------------------------------------------------------------------------- KeyboardInterrupt Traceback (most recent call last) <ipython-input-22-d3717313b1f5> in <module> 1 for t in range(NUM_EPOCHS): 2 print(f"Epoch {t+1}\n-------------------------------") ----> 3 train_loop(trainloader, model, loss_fn, optimizer) 4 print(generate(model, 'ใคร', int_to_vocab, predict_len=100)) 5 print("Done!") <ipython-input-21-5a29d62dc645> in train_loop(dataloader, model, loss_fn, optimizer) 10 # Backpropagation 11 optimizer.zero_grad() ---> 12 loss.backward() 13 optimizer.step() 14 /usr/local/lib/python3.7/dist-packages/torch/_tensor.py in backward(self, gradient, retain_graph, create_graph, inputs) 394 create_graph=create_graph, 395 inputs=inputs) --> 396 torch.autograd.backward(self, gradient, retain_graph, create_graph, inputs=inputs) 397 398 def register_hook(self, hook): /usr/local/lib/python3.7/dist-packages/torch/autograd/__init__.py in backward(tensors, grad_tensors, retain_graph, create_graph, grad_variables, inputs) 173 Variable._execution_engine.run_backward( # Calls into the C++ engine to run the backward pass 174 tensors, grad_tensors_, retain_graph, create_graph, inputs, --> 175 allow_unreachable=True, accumulate_grad=True) # Calls into the C++ engine to run the backward pass 176 177 def grad( KeyboardInterrupt:
from itertools import chain
from collections import Counter
import requests
from bs4 import BeautifulSoup
def scrape_siamzone_url(d):
soup = BeautifulSoup(requests.get('https://www.siamzone.com/music/thailyric/%d' % d).content, 'html.parser')
title, artist_name = soup.find('title').text.split('|')
title, artist_name = title.strip(), artist_name.strip()
n_shares = int(soup.find('span', attrs={'class': 'sz-social-number'}).text.replace(',', ''))
full_lyrics = soup.find('div', attrs={'itemprop': 'articleBody'}).text.strip()
return {
'url': 'https://www.siamzone.com/music/thailyric/%d' % d,
'soup': soup,
'title': title,
'artist_name': artist_name,
'n_shares': n_shares,
'full_lyrics': full_lyrics
}
def scrape_siamzone():
data = []
for i in range(14050, 16041):
try:
data.append(scrape_siamzone_url(i))
except:
pass
df = pd.DataFrame(data)
df['lyrics'] = df.full_lyrics.map(clean_lyrics)
return df