Single-Output Regression Neural Network pada kasus Prediksi Kualitas Air

Notebook ini hanya contoh dan dibuat untuk pembelajaran mengenai Deep Learning/Neural Networks dan penggunaan praktis bidang sumberdaya air menggunakan Python. Notebook ini masih perlu dievaluasi kembali jika digunakan untuk kepenting riset/penelitian ataupun proyek.

Informasi Notebook

  • notebook name: taruma_demo_ann_ka_2.0.0
  • notebook version/date: 2.0.0/20190713
  • notebook server: Google Colab
  • hidrokit version: 0.2.0
  • python version: 3.7

Deskripsi Kasus

Bagian ini akan menjelaskan gambaran umum mengenai dataset, permasalahan/tujuan, dan langkah penyelesaiannya

Dataset

Dataset merupakan data kualitas air bulanan dari Juni 2000 hingga Desember 2017 (211 data bulanan). Dataset memiliki 15 kolom yaitu (berurutan):

  • 1 kolom berupa tanggal
  • 11 kolom independent variables yang diperoleh di stasiun A:
    • temperatur udara (temp_udara), lama penyinaran (lama_sinar), kecepatan angin (kec_angin), $Q_{in}$ (debit_masuk), $Q_{out}$ (debit_keluar), volume (volume), temperatur air (temp_air), $O_2$, Oksigen (oksigen), $NO_2$, Nitrogen (nitrogen), $NO_3$, Nitrat (nitrat), $NH_3$, Amonia (amonia).
  • 3 kolom dependent variables pada stasiun B:
    • $NO_2$, Nitrogen (out_nitrogen), $NO_3$, Nitrat (out_nitrat), $NH_3$, Amonia (out_amonia).

Permasalahan dan Tujuan

Permasalahan:

  • Peneliti ingin mengetahui nilai tiga kualitas air berupa $NO_2, NO_3, NH_3$ pada stasiun B berdasarkan informasi yang diperoleh di stasiun A.

Batasan Masalah:

  • Dalam notebook ini target yang digunakan hanya $NH_3$ (single-output).
  • Arsitektur Neural Networks yang digunakan adalah Multi Layer Perceptron (MLP), dengan setidaknya hidden layers lebih dari satu.
  • Kasus disini merupakan contoh permasalahan supervised learning.

Pertanyaan:

  • Berapa nilai NH3 pada stasiun B pada waktu $t$ jika telah diketahui hasil pengukuran di stasiun A pada waktu $t$ dan hasil observasi stasiun B pada waktu sebelumnya ($t-1, t-2, ..., t-n$, dengan $n$ adalah jumlah timesteps)?
  • Apa arsitektur NN yang optimal untuk melakukan prediksi kualitas air?

Catatan: Dalam kasus ini akan digunakan $2$ timesteps yang berarti data dua bulan yang lalu akan digunakan sebagai input untuk memprediksikan target pada waktu $t$.

Strategi Penyelesaian Masalah

Tahap 1: Data exploration and Data munging

  1. Import dataset dari berkas excel ke dalam pandas.DataFrame.
  2. Eksplorasi dataset berupa memeriksa kehilangan data disertai validasi dan verifikasi data. Data exploration dapat berupa visualisasi, deskripsi statistik, dan memeriksa jika terdapat nilai outlier atau tidak.
  3. Jika terdapat data yang hilang, maka diasumsikan data memiliki sifat linear sehingga data yang hilang diisi dengan menginterpolasikan dengan metode linear. (Langkah ini dapat diganti dengan kesesuaian keahlian bidangnya)
  4. Diperiksa kembali data yang telah diisi dan melakukan ekplorasi data lagi untuk memastikan dataset sudah siap untuk diolah di tahap berikutnya.

Tahap 2: Data Preprocessing

  1. Membagi dataset menjadi dua bagian yaitu training set dan test set. Pemotongan data ini tidak acak. Training set merupakan potongan data dari Juni 2000 hingga Desember 2014. Test set merupakan potongan data dari Januari 2015 hingga Desember 2017. Untuk selanjutnya hanya training set yang akan diproses untuk training dan test set disimpan untuk tahap evaluasi model.
  2. Dilakukan tahap Scaling yaitu menormalisasikan nilai pada dataset ke dalam skala yang sama. Digunakan metode MinMaxScaler karena diketahui data tidak terdistribusi normal.
  3. Membuat kolom yang menunjukkan $2$ timesteps, sehingga dataset yang sebelumnya 14 kolom menjadi $feature_{original}\times(timesteps+1)=14\times(2+1)=42$.
  4. Menampilkan hasil penambahan timesteps dalam bentuk pandas.DataFrame.
  5. training set dibagi menjadi X_train dan y_train. Dengan X_train merupakan seluruh kolom kecuali nilai observasi dari stasiun B pada waktu $t$ (kolom: out_nitrogen_tmin0, out_nitrat_tmin0, out_amonia_tmin0) sebagai feature. Sedangkan y_train merupakan kolom out_amonia_tmin0 sebagai target.

Tahap 3: Building Neural Networks

  1. Untuk Neural Networks digunakan Multi Layer Perceptron (MLP) dengan target single-output.
  2. Membuat fungsi build_model yang memberikan fleksibilitas arsitektur sehingga memiliki hidden layer lebih dari satu dengan parameter hidden_layers.
  3. Menggunakan GridSearchCV untuk mengetahui parameter terbaik.
  4. Melalukan proses fit terhadap X_train, y_train. Besarnya validation split yang digunakan sebesar $0.2$.

Tahap 4: Evaluation Models

  1. Pada tahap evaluasi, dataset yang digunakan adalah test set. test set yang diperoleh dari pembagian set perlu diproses lebih lanjut sebelum digunakan untuk memprediksi.
  2. Dilakukan proses data preprocessing seperti tahap 2 langkah 2-5 pada test set.
  3. Memprediksi nilai dengan model yang terbaik hasil Grid Search.
  4. Mengembalikan nilai prediksi dan nilai y_test ke skala original dengan menggunakan method inverse_transform. Hal ini dilakukan dengan mentransfer atribut MinMaxScaler sebelumnya ke object baru, agar menyederhanakan proses pengembalian ke skala asli.
  5. Evaluasi menggunakan metrik RMSE/MAE.
  6. Evaluasi menggunakan grafik.
  7. Menghitung nilai beda prediksi dan observasi, dan dilakukan statistik deskriptif disertai membuat histogram nilai residu tersebut.

Tahap 5: Conclusion and Interpretation

  1. Tahap ini menyimpulkan hasil model dan menginterpretasikan model NN.
  2. Langkah yang bisa meningkatkan hasil model dengan mengurangi kemungkinan overfitting ataupun underfitting.

Tahap 0: Pengaturan Awal

Bagian ini merupakan pengaturan awal pribadi, dapat diabaikan.

In [1]:
from datetime import datetime

#### PROJECT DESCRIPTION
notebook_version = '2.0.0'
notebook_title = 'kualitas_air_ann_so' + '_' + notebook_version
prefix = datetime.utcnow().strftime("%Y%m%d_%H%M")
project_title = prefix + '_' + notebook_title

print(f'Judul Notebook: {notebook_title}')
print(f'Judul Proyek: {project_title}')
Judul Notebook: kualitas_air_ann_so_2.0.0
Judul Proyek: 20190713_0507_kualitas_air_ann_so_2.0.0
In [0]:
#### Memasang Akses Google Drive (untuk tempat menyimpan hasil training)
from google.colab import drive
drive.mount('/content/gdrive')
drop_path = '/content/gdrive/My Drive/Colab Notebooks/_dropbox'
In [3]:
#### Instalasi Paket Python Pribadi (untuk Logging)
#### https://github.com/taruma/umakit
!pip install umakit
from umakit.logtool import LogTool
mylog = LogTool()
mylog._reset()

#### Instalasi Paket Hidrokit (untuk plotting, dan transformasi kolom saat data preprocessing)
#### https://github.com/taruma/hidrokit
!pip install hidrokit
Collecting umakit
  Downloading https://files.pythonhosted.org/packages/76/64/d972d8fd9936a7a498c75e593d1986ec4d19204c84a636e5b5783c34806e/umakit-0.1.1-py3-none-any.whl
Installing collected packages: umakit
Successfully installed umakit-0.1.1
Collecting hidrokit
  Downloading https://files.pythonhosted.org/packages/43/9d/343d2a413a07463a21dd13369e31d664d6733bbfd46276abef5d804c83d1/hidrokit-0.2.0-py2.py3-none-any.whl
Requirement already satisfied: matplotlib in /usr/local/lib/python3.6/dist-packages (from hidrokit) (3.0.3)
Requirement already satisfied: pandas in /usr/local/lib/python3.6/dist-packages (from hidrokit) (0.24.2)
Requirement already satisfied: numpy in /usr/local/lib/python3.6/dist-packages (from hidrokit) (1.16.4)
Requirement already satisfied: python-dateutil>=2.1 in /usr/local/lib/python3.6/dist-packages (from matplotlib->hidrokit) (2.5.3)
Requirement already satisfied: cycler>=0.10 in /usr/local/lib/python3.6/dist-packages (from matplotlib->hidrokit) (0.10.0)
Requirement already satisfied: kiwisolver>=1.0.1 in /usr/local/lib/python3.6/dist-packages (from matplotlib->hidrokit) (1.1.0)
Requirement already satisfied: pyparsing!=2.0.4,!=2.1.2,!=2.1.6,>=2.0.1 in /usr/local/lib/python3.6/dist-packages (from matplotlib->hidrokit) (2.4.0)
Requirement already satisfied: pytz>=2011k in /usr/local/lib/python3.6/dist-packages (from pandas->hidrokit) (2018.9)
Requirement already satisfied: six>=1.5 in /usr/local/lib/python3.6/dist-packages (from python-dateutil>=2.1->matplotlib->hidrokit) (1.12.0)
Requirement already satisfied: setuptools in /usr/local/lib/python3.6/dist-packages (from kiwisolver>=1.0.1->matplotlib->hidrokit) (41.0.1)
Installing collected packages: hidrokit
Successfully installed hidrokit-0.2.0

Tahap 1: Data Exploration and Data Munging

Ringkasan pada tahap ini:

  1. Import dataset.
  2. Eksplorasi dataset.
  3. Mengisi data yang hilang.
  4. Eksplorasi dataset kembali.

1.1 Import dataset

In [4]:
#### Mengunggah dataset
from google.colab import files
uploaded = files.upload()
Upload widget is only available when the cell has been executed in the current browser session. Please rerun this cell to enable.
Saving data_ka.xlsx to data_ka.xlsx
In [5]:
#### 1. Import dataset ke pandas.DataFrame
import pandas as pd

## import dataset
dataset = pd.read_excel('data_ka.xlsx', skiprows=[0])

## Menamai kolom
dataset.columns = ["date", "temp_udara", "lama_sinar", "kec_angin", 
                   "debit_masuk", "debit_keluar", "volume", "temp_air", 
                   "oksigen", "nitrogen", "nitrat", "amonia", 
                   "out_nitrogen", "out_nitrat", "out_amonia"]

## Mengatur index dataframe ke tanggal
dataset = dataset.set_index('date')

dataset.head(5)
Out[5]:
temp_udara lama_sinar kec_angin debit_masuk debit_keluar volume temp_air oksigen nitrogen nitrat amonia out_nitrogen out_nitrat out_amonia
date
2000-06-20 26.5 6.050000 1.4 91.35 168.32 2.334285e+09 23.937500 5.062500 0.235750 0.481875 0.825375 0.326915 0.524643 0.589523
2000-07-20 25.2 4.366667 19.6 168.52 170.14 2.203561e+09 26.125000 5.237500 0.570500 0.620375 0.689125 0.730400 0.727771 0.602750
2000-08-20 26.9 7.616667 92.5 181.32 154.55 2.019619e+09 26.125000 4.912500 0.570500 0.620375 0.688750 0.700556 0.742150 0.627844
2000-09-20 26.4 5.100000 88.4 133.62 175.08 1.991705e+09 23.416667 3.145833 0.390292 0.463708 0.569583 0.175556 0.457229 0.430150
2000-10-20 25.2 6.233333 12.1 168.56 168.64 1.742853e+09 24.708333 3.739583 0.547202 0.531271 0.671292 0.583651 0.461479 0.559891

1.2 Eksplorasi dataset

In [6]:
## Gamabaran umum dataset
dataset.info()
<class 'pandas.core.frame.DataFrame'>
DatetimeIndex: 211 entries, 2000-06-20 to 2017-12-13
Data columns (total 14 columns):
temp_udara      210 non-null float64
lama_sinar      210 non-null float64
kec_angin       210 non-null float64
debit_masuk     210 non-null float64
debit_keluar    210 non-null float64
volume          210 non-null float64
temp_air        209 non-null float64
oksigen         210 non-null float64
nitrogen        189 non-null float64
nitrat          208 non-null float64
amonia          210 non-null float64
out_nitrogen    210 non-null float64
out_nitrat      208 non-null float64
out_amonia      210 non-null float64
dtypes: float64(14)
memory usage: 24.7 KB

Dari dataset dapat disimpulkan:

  1. Kolom date digunakan sebagai acuan total jumlah data yang tersedia. Diketahui bahwa ada 211 data.
  2. Kolom temp_udara, lama_sinar, kec_angin, debit_masuk, debit_keluar, volume, temp_air, amonia, out_nitrogen, out_amonia kehilangan 1 data, sehingga jumlah datanya hanya 210.
  3. Kolom berikut memiliki kehilangan data lebih dari 1 data:
    • temp_air: total: 209 data, kehilangan 2 data.
    • nitrogen: total: 189 data, kehilangan 22 data.
    • nitrat: total: 208 data, kehilangan 3 data.
    • out_nitrat: total: 208 data, kehilangan 3 data.

Visualisasi

In [7]:
from hidrokit.viz import graph
graph.subplots(dataset, ncols=1, nrows=14, figsize=(15, 15));

Dari grafik diatas terlihat ada data yang hilang.

1.3 Kehilangan Data

In [8]:
## Memperoleh informasi kehilangan data
from hidrokit.prep import read

data_hilang = read.missing_row(dataset, date_format='%Y/%m')

print("Daftar kehilangan data:")
for column, value in data_hilang.items():
    print(
        "Kolom {}: {}".format(column, value)
    )
Daftar kehilangan data:
Kolom temp_udara: ['2005/12']
Kolom lama_sinar: ['2005/12']
Kolom kec_angin: ['2005/12']
Kolom debit_masuk: ['2005/12']
Kolom debit_keluar: ['2005/12']
Kolom volume: ['2005/12']
Kolom temp_air: ['2005/11', '2005/12']
Kolom oksigen: ['2005/12']
Kolom nitrogen: ['2005/12', '2007/04', '2007/05', '2007/06', '2007/07', '2007/08', '2007/09', '2007/10', '2007/11', '2007/12', '2008/01', '2008/02', '2008/03', '2008/04', '2008/05', '2008/06', '2008/07', '2008/08', '2008/09', '2008/10', '2008/11', '2008/12']
Kolom nitrat: ['2005/06', '2005/07', '2005/12']
Kolom amonia: ['2005/12']
Kolom out_nitrogen: ['2005/12']
Kolom out_nitrat: ['2005/06', '2005/07', '2005/12']
Kolom out_amonia: ['2005/12']

Dari proses diatas diperoleh informasi:

  1. Pada bulan 2005/12 (Desember 2005), data tidak tersedia pada seluruh kolom.
  2. Kolom temp_air, kehilangan data pada 2005/11 (November 2005).
  3. Kolom nitrogen, kehilangan data pada dari 2007/04 (April 2007) sampai 2008/12 (Desember 2008).
  4. Kolom nitrat dan out_amonia, kehilangan data pada 2005/06 (Juni 2005) dan 2005/07 (Juli 2005).`

Mengisi Kehilangan data

Diasumsikan bahwa data yang hilang dapat diisi dengan metode interpolasi linear. Dengan catatan metode tersebut digunakan dalam notebook ini untuk pembelajaran. Dimungkinkan untuk mengisi data hilang dengan teknik yang tersedia dalam bidang keahliannya.

In [0]:
## Mengisi Data yang hilang dengan metode linear
new_dataset = dataset.interpolate(method='linear')

1.4 Eksplorasi dataset

In [10]:
# statistik deskriptif
new_dataset.describe()
Out[10]:
temp_udara lama_sinar kec_angin debit_masuk debit_keluar volume temp_air oksigen nitrogen nitrat amonia out_nitrogen out_nitrat out_amonia
count 211.000000 211.000000 211.000000 211.000000 211.000000 2.110000e+02 211.000000 211.000000 211.000000 211.000000 211.000000 211.000000 211.000000 211.000000
mean 26.297867 5.429739 89.090929 192.909614 181.526661 2.184541e+09 27.805279 4.398493 0.144310 0.354018 0.391544 0.124222 0.369451 0.258836
std 1.230595 2.653354 56.941944 129.691401 80.164627 4.316360e+08 1.592180 1.675356 0.322774 0.530511 0.709351 0.216860 0.591848 0.360054
min 22.400000 0.000000 0.400000 4.100000 57.942000 9.531267e+08 20.897500 1.053250 0.000000 0.004000 0.000000 0.000000 0.004667 0.000000
25% 25.750000 3.591667 47.350000 99.710000 135.915000 1.895588e+09 26.882917 3.125875 0.010000 0.070833 0.002958 0.006673 0.052589 0.002378
50% 26.200000 5.750000 83.300000 158.800000 170.140000 2.224052e+09 27.812500 4.166667 0.022000 0.187000 0.089250 0.019494 0.143512 0.066165
75% 27.000000 7.683333 125.400000 264.170000 201.090000 2.548267e+09 28.650000 5.683333 0.149181 0.540000 0.529875 0.085891 0.452636 0.448939
max 29.150000 10.083333 365.900000 879.730000 673.060000 2.822702e+09 32.916667 9.000000 3.700000 5.822500 4.583333 1.164635 5.460060 2.252332

Visualisasi

In [11]:
graph.subplots(new_dataset, ncols=1, nrows=14, figsize=(15, 15));
In [12]:
## Pairplot untuk melihat hubungan masing-masing kolom
import seaborn as sns

g = sns.pairplot(new_dataset)
g.fig.set_size_inches(15,15)