├── LICENSE.md ├── README.md ├── ansible ├── anaconda.yml ├── ansible.cfg ├── deploy ├── desktop.yml ├── hosts └── roles │ ├── anaconda │ └── tasks │ │ └── main.yml │ ├── base │ └── tasks │ │ └── main.yml │ └── desktop │ └── tasks │ └── main.yml ├── caffe_sentiment_analysis ├── convert.py ├── create_lmdb.sh ├── nn.prototxt ├── nn_solver.prototxt ├── prepare_data.sh └── train.sh ├── cat ├── Detect Cats.ipynb ├── convert.sh ├── crop.py ├── extract_label.py ├── preprocess.sh ├── requirements.txt └── setup_repo.sh ├── google-forms ├── Code.gs ├── output │ ├── cat.txt │ ├── dog.txt │ ├── survey.tsv │ └── urls.tsv └── prepare.py ├── gspread ├── requirements.txt ├── start_update.sh └── update.py ├── ipynb ├── The First Tour of the IPython Notebook.ipynb └── requirements.txt ├── plurk_crawler └── crawl.go ├── sacred ├── Sampled MNIST.ipynb ├── mnist.py ├── requirements.txt ├── run.sh └── softmax.py ├── scrapy ├── PTT Analysis.ipynb ├── ptt │ ├── __init__.py │ ├── items.py │ ├── pipelines.py │ ├── settings.py │ └── spiders │ │ ├── __init__.py │ │ └── ptt.py ├── requirements.txt └── scrapy.cfg ├── sentiment_analysis ├── evaluate.py ├── normalize.py ├── preprocess.py ├── run.sh ├── split.py ├── tfidf.py ├── train.py └── transform.py ├── spark_word2vec ├── get_data.sh ├── install.sh ├── run.sh └── sparkw2v │ ├── sparkw2v.sbt │ └── src │ └── main │ └── scala │ └── SparkW2V.scala ├── word2vec_tw ├── cut.py ├── extract_json.py └── start.sh └── zarr-dataset ├── clean_anime_faces.sh ├── convert_anime_faces.py ├── environment.yml ├── run.sh └── test_anime_faces.py /LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Shaform 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # experiments 2 | Some research experiments I have done during the years. 3 | 4 | Most of the notes can be found on [City of Wings](https://city.shaform.com/en/category/notes/). 5 | -------------------------------------------------------------------------------- /ansible/anaconda.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - hosts: '{{ hosts }}' 4 | roles: 5 | - anaconda 6 | -------------------------------------------------------------------------------- /ansible/ansible.cfg: -------------------------------------------------------------------------------- 1 | [defaults] 2 | hostfile = hosts 3 | -------------------------------------------------------------------------------- /ansible/deploy: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | HOST=${1:-localhost} 3 | YML=${2:-desktop.yml} 4 | ansible-playbook --ask-become-pass --extra-vars="hosts=$HOST" $YML 5 | -------------------------------------------------------------------------------- /ansible/desktop.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - hosts: '{{ hosts }}' 4 | become: true 5 | become_user: root 6 | roles: 7 | - base 8 | - desktop 9 | -------------------------------------------------------------------------------- /ansible/hosts: -------------------------------------------------------------------------------- 1 | [host1] 2 | 192.168.1.2 3 | 4 | [host2] 5 | 192.168.1.3 ansible_ssh_user=bob ansible_ssh_private_key_file=~/.ssh/bob.pem 6 | 7 | [servers] 8 | 192.168.1.4 9 | 192.168.1.5 10 | 192.168.1.6 11 | -------------------------------------------------------------------------------- /ansible/roles/anaconda/tasks/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: download Anaconda 4 | get_url: 5 | url: 'https://repo.continuum.io/archive/Anaconda2-4.3.1-Linux-x86_64.sh' 6 | dest: /tmp/Anaconda2-4.3.1-Linux-x86_64.sh 7 | mode: '0755' 8 | 9 | - name: install Anaconda 10 | command: bash /tmp/Anaconda2-4.3.1-Linux-x86_64.sh -b -p {{ ansible_env.HOME }}/anaconda2 11 | args: 12 | creates: '{{ ansible_env.HOME}}/anaconda2' 13 | 14 | - name: add Anaconda to PATH 15 | lineinfile: 16 | name: '{{ ansible_env.HOME }}/.bashrc' 17 | line: 'export PATH=$HOME/anaconda2/bin:${PATH}' 18 | -------------------------------------------------------------------------------- /ansible/roles/base/tasks/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: mkdir for configure files 4 | file: 5 | dest: '{{ item.dest }}' 6 | owner: root 7 | group: root 8 | mode: '{{ item.mode | default("0755") }}' 9 | state: directory 10 | 11 | with_items: 12 | - dest: /etc/sudoers.d 13 | 14 | - name: set sudo umask 15 | copy: 16 | dest: '{{ item.dest }}' 17 | owner: root 18 | group: root 19 | mode: '{{ item.mode | default("0644") }}' 20 | content: '{{ item.content }}' 21 | 22 | with_items: 23 | - dest: /etc/sudoers.d/umask 24 | mode: '0400' 25 | content: | 26 | # This file is managed by Ansible. DO NOT EDIT. 27 | 28 | Defaults umask_override 29 | Defaults umask = 0002 30 | 31 | - name: upgrade packages 32 | apt: 33 | upgrade: dist 34 | update_cache: yes 35 | 36 | - name: install core packages 37 | apt: 38 | name: '{{ item.name }}' 39 | state: '{{ item.state | default("present") }}' 40 | purge: yes 41 | 42 | with_items: 43 | - name: git-core 44 | - name: gufw 45 | - name: htop 46 | - name: p7zip-full 47 | - name: p7zip-rar 48 | - name: screen 49 | - name: tmux 50 | - name: yadm 51 | -------------------------------------------------------------------------------- /ansible/roles/desktop/tasks/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: mkdir for configure files 4 | file: 5 | dest: '{{ item.dest }}' 6 | owner: root 7 | group: root 8 | mode: '{{ item.mode | default("0755") }}' 9 | state: directory 10 | 11 | with_items: 12 | - dest: /usr/share/lightdm/lightdm.conf.d 13 | 14 | - name: disable guest login 15 | copy: 16 | dest: '{{ item.dest }}' 17 | owner: root 18 | group: root 19 | mode: '{{ item.mode | default("0644") }}' 20 | content: '{{ item.content }}' 21 | 22 | with_items: 23 | - dest: /usr/share/lightdm/lightdm.conf.d/50-no-guest.conf 24 | mode: '0400' 25 | content: | 26 | # This file is managed by Ansible. DO NOT EDIT. 27 | 28 | [SeatDefaults] 29 | allow-guest=false 30 | 31 | - name: install desktop apps 32 | apt: 33 | name: '{{ item.name }}' 34 | state: '{{ item.state | default("present") }}' 35 | purge: yes 36 | update_cache: yes 37 | 38 | with_items: 39 | - name: ibus-chewing 40 | - name: keepassx 41 | - name: libappindicator1 # For Dropbox 42 | - name: python-gpgme # For Dropbox 43 | - name: nautilus-dropbox 44 | - name: workrave 45 | 46 | - name: install Google Chrome 47 | apt: 48 | deb: 'https://dl.google.com/linux/direct/google-chrome-stable_current_amd64.deb' 49 | update_cache: yes 50 | -------------------------------------------------------------------------------- /caffe_sentiment_analysis/convert.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | import argparse 3 | import random 4 | 5 | import lmdb 6 | import numpy as np 7 | from caffe.io import array_to_datum 8 | 9 | num_of_dims = 100 10 | 11 | 12 | def load_data(path): 13 | items = [] 14 | with open(path) as f: 15 | for l in f: 16 | tokens = l.rstrip().split() 17 | label = int(tokens[0]) 18 | # change label `-1' to `0' 19 | if label == -1: 20 | label = 0 21 | # ignore the index since we already know the format 22 | arr = [float(dim.split(':')[1]) for dim in tokens[1:]] 23 | items.append((label, arr)) 24 | 25 | random.shuffle(items) 26 | 27 | Y = np.array([y for y, _ in items]) 28 | X = np.array([x for _, x in items]) 29 | X = X.reshape((len(Y), 1, 1, num_of_dims)) 30 | 31 | return X, Y 32 | 33 | 34 | def save_data(path, X, Y): 35 | num = np.prod(X.shape) 36 | itemsize = np.dtype(X.dtype).itemsize 37 | # set a reasonable upper limit for database size 38 | map_size = 10240 * 1024 + num * itemsize * 2 39 | print 'save {} instances...'.format(num) 40 | 41 | env = lmdb.open(path, map_size=map_size) 42 | 43 | for i, (x, y) in enumerate(zip(X, Y)): 44 | datum = array_to_datum(x, y) 45 | str_id = '{:08}'.format(i) 46 | 47 | with env.begin(write=True) as txn: 48 | txn.put(str_id, datum.SerializeToString()) 49 | 50 | 51 | def process_commands(): 52 | parser = argparse.ArgumentParser() 53 | parser.add_argument('input') 54 | parser.add_argument('output') 55 | 56 | return parser.parse_args() 57 | 58 | 59 | def main(): 60 | args = process_commands() 61 | X, Y = load_data(args.input) 62 | save_data(args.output, X, Y) 63 | 64 | if __name__ == '__main__': 65 | main() 66 | -------------------------------------------------------------------------------- /caffe_sentiment_analysis/create_lmdb.sh: -------------------------------------------------------------------------------- 1 | python convert.py full-train.txt movie-train-lmdb 2 | python convert.py test.txt movie-test-lmdb 3 | -------------------------------------------------------------------------------- /caffe_sentiment_analysis/nn.prototxt: -------------------------------------------------------------------------------- 1 | name: "MovieNet" 2 | layer { 3 | name: "movie" 4 | type: "Data" 5 | top: "data" 6 | top: "label" 7 | include { 8 | phase: TRAIN 9 | } 10 | data_param { 11 | source: "movie-train-lmdb" 12 | batch_size: 250 13 | backend: LMDB 14 | } 15 | } 16 | layer { 17 | name: "movie" 18 | type: "Data" 19 | top: "data" 20 | top: "label" 21 | include { 22 | phase: TEST 23 | } 24 | data_param { 25 | source: "movie-test-lmdb" 26 | batch_size: 250 27 | backend: LMDB 28 | } 29 | } 30 | layer { 31 | name: "ip1" 32 | type: "InnerProduct" 33 | bottom: "data" 34 | top: "ip1" 35 | inner_product_param { 36 | num_output: 100 37 | weight_filler { 38 | type: "xavier" 39 | } 40 | bias_filler { 41 | type: "constant" 42 | } 43 | } 44 | } 45 | layer { 46 | name: "relu1" 47 | type: "ReLU" 48 | bottom: "ip1" 49 | top: "ip1" 50 | } 51 | layer { 52 | name: "ip2" 53 | type: "InnerProduct" 54 | bottom: "ip1" 55 | top: "ip2" 56 | inner_product_param { 57 | num_output: 2 58 | weight_filler { 59 | type: "xavier" 60 | } 61 | bias_filler { 62 | type: "constant" 63 | } 64 | } 65 | } 66 | layer { 67 | name: "accuracy" 68 | type: "Accuracy" 69 | bottom: "ip2" 70 | bottom: "label" 71 | top: "accuracy" 72 | include { 73 | phase: TEST 74 | } 75 | } 76 | layer { 77 | name: "loss" 78 | type: "SoftmaxWithLoss" 79 | bottom: "ip2" 80 | bottom: "label" 81 | top: "loss" 82 | } 83 | -------------------------------------------------------------------------------- /caffe_sentiment_analysis/nn_solver.prototxt: -------------------------------------------------------------------------------- 1 | # The train/test net protocol buffer definition 2 | net: "nn.prototxt" 3 | # test_iter specifies how many forward passes the test should carry out. 4 | # We have test batch size 250 and 100 test iterations, 5 | # covering the full 25,000 testing reviews. 6 | test_iter: 100 7 | # Carry out testing every 500 training iterations. 8 | test_interval: 500 9 | # The base learning rate, momentum and the weight decay of the network. 10 | base_lr: 0.01 11 | momentum: 0.9 12 | weight_decay: 0.0005 13 | # The learning rate policy 14 | lr_policy: "inv" 15 | gamma: 0.0001 16 | power: 0.75 17 | # Display every 100 iterations 18 | display: 100 19 | # The maximum number of iterations 20 | max_iter: 10000 21 | snapshot_prefix: "nn" 22 | # solver mode: CPU or GPU 23 | solver_mode: GPU 24 | -------------------------------------------------------------------------------- /caffe_sentiment_analysis/prepare_data.sh: -------------------------------------------------------------------------------- 1 | # get mesnilgr/iclr15 2 | git clone https://github.com/mesnilgr/iclr15 3 | 4 | mkdir -p iclr15_run 5 | cd iclr15_run 6 | 7 | # get data 8 | ../iclr15/scripts/data.sh 9 | 10 | # extract the part to create paragraph vectors from iclr15 scripts 11 | sed -e '/liblinear/,$d' ../iclr15/scripts/paragraph.sh > paragraph.sh 12 | 13 | # start creating the vectors 14 | chmod +x paragraph.sh 15 | ./paragraph.sh 16 | 17 | # copy the vectors 18 | cd word2vec 19 | cp full-train.txt test.txt ../../ 20 | -------------------------------------------------------------------------------- /caffe_sentiment_analysis/train.sh: -------------------------------------------------------------------------------- 1 | caffe train --solver=nn_solver.prototxt 2 | -------------------------------------------------------------------------------- /cat/convert.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | python3 extract_label.py --indir labels --outdir data 3 | -------------------------------------------------------------------------------- /cat/crop.py: -------------------------------------------------------------------------------- 1 | import argparse 2 | import os 3 | 4 | from PIL import Image 5 | 6 | image_extensions = {'png', 'jpg', 'jpeg'} 7 | 8 | 9 | def crop_center(img, width, height): 10 | x = img.size[0] // 2 11 | y = img.size[1] // 2 12 | 13 | x1 = x - (width // 2) 14 | x2 = x + (width - width // 2) 15 | 16 | y1 = y - (height // 2) 17 | y2 = y + (height - height // 2) 18 | 19 | try: 20 | return img.crop((x1, y1, x2, y2)) 21 | except OSError as e: 22 | print(e) 23 | return None 24 | 25 | 26 | def main(indir, outdir, width, height): 27 | for fname in os.listdir(indir): 28 | parts = fname.rsplit('.', 1) 29 | if len(parts) == 2 and parts[1] in image_extensions: 30 | name, ext = parts 31 | img = Image.open(os.path.join(indir, fname)) 32 | 33 | if img.size[0] >= width and img.size[1] >= height: 34 | img = crop_center(img, width, height) 35 | if img: 36 | img.save(os.path.join(outdir, name + '.png')) 37 | 38 | 39 | if __name__ == '__main__': 40 | parser = argparse.ArgumentParser() 41 | parser.add_argument('--indir', required=True) 42 | parser.add_argument('--outdir', required=True) 43 | parser.add_argument('--width', type=int, default=608) 44 | parser.add_argument('--height', type=int, default=608) 45 | 46 | args = parser.parse_args() 47 | 48 | main( 49 | indir=args.indir, 50 | outdir=args.outdir, 51 | width=args.width, 52 | height=args.height) 53 | -------------------------------------------------------------------------------- /cat/extract_label.py: -------------------------------------------------------------------------------- 1 | import argparse 2 | import json 3 | import os 4 | import random 5 | import xml.etree.ElementTree as ET 6 | 7 | 8 | def main(indir, outdir, name, seed): 9 | random.seed(seed) 10 | if name is None: 11 | name = os.path.basename(indir) 12 | 13 | entries = [] 14 | 15 | for path in sorted(os.listdir(indir)): 16 | if path.endswith('.xml'): 17 | tree = ET.parse(os.path.join(indir, path)) 18 | root = tree.getroot() 19 | 20 | img_path = root.findtext('path') 21 | rects = [] 22 | 23 | for obj in root.iter('object'): 24 | x1 = float(obj.find('bndbox').findtext('xmin')) 25 | y1 = float(obj.find('bndbox').findtext('ymin')) 26 | x2 = float(obj.find('bndbox').findtext('xmax')) 27 | y2 = float(obj.find('bndbox').findtext('ymax')) 28 | 29 | rects.append({'x1': x1, 'x2': x2, 'y1': y1, 'y2': y2}) 30 | 31 | entry = {'image_path': img_path, 'rects': rects} 32 | entries.append(entry) 33 | 34 | random.shuffle(entries) 35 | 36 | train_offset = int(len(entries) * 0.8) 37 | 38 | train = entries[:train_offset] 39 | val = entries[train_offset:] 40 | 41 | datasets = [('train', train), ('val', val)] 42 | 43 | for prefix_name, dataset in datasets: 44 | with open( 45 | os.path.join(outdir, '{}_{}_boxes.json'.format( 46 | name, prefix_name)), 'w') as f: 47 | json.dump(dataset, f, indent=4, separators=(',', ': ')) 48 | 49 | 50 | if __name__ == '__main__': 51 | parser = argparse.ArgumentParser() 52 | parser.add_argument('--indir', required=True) 53 | parser.add_argument('--outdir', required=True) 54 | 55 | parser.add_argument('--name') 56 | parser.add_argument('--seed', type=int, default=633) 57 | 58 | args = parser.parse_args() 59 | 60 | main(indir=args.indir, outdir=args.outdir, name=args.name, seed=args.seed) 61 | -------------------------------------------------------------------------------- /cat/preprocess.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | mkdir -p data/train 3 | mkdir -p data/test 4 | python3 crop.py --indir cats/catmapper --outdir data/train 5 | python3 crop.py --indir cats/cat_photos --outdir data/test 6 | -------------------------------------------------------------------------------- /cat/requirements.txt: -------------------------------------------------------------------------------- 1 | Pillow==6.2.0 2 | -------------------------------------------------------------------------------- /cat/setup_repo.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | git clone https://github.com/maxogden/cats 3 | git clone https://github.com/Russell91/TensorBox 4 | git clone https://github.com/tzutalin/labelImg 5 | -------------------------------------------------------------------------------- /google-forms/Code.gs: -------------------------------------------------------------------------------- 1 | function getSurveyData(sheetName) { 2 | var arrayOfArrays = SpreadsheetApp.getActiveSpreadsheet().getSheetByName(sheetName || 'survey').getDataRange().getValues(); 3 | var headers = arrayOfArrays.shift(); 4 | return arrayOfArrays; 5 | } 6 | 7 | function getUrlData(sheetName) { 8 | var arrayOfArrays = SpreadsheetApp.getActiveSpreadsheet().getSheetByName(sheetName || 'urls').getDataRange().getValues(); 9 | var headers = arrayOfArrays.shift(); 10 | var mapOfUrls = {}; 11 | for (var i=0; i=4.4.1 2 | gspread>=0.3.0 3 | oauth2client>=2.0.1 4 | pyOpenSSL>=0.15.1 5 | -------------------------------------------------------------------------------- /gspread/start_update.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | curr_dir=`dirname "$BASH_SOURCE"` 3 | 4 | cd $curr_dir ; 5 | source my_env/bin/activate 6 | python update.py 7 | -------------------------------------------------------------------------------- /gspread/update.py: -------------------------------------------------------------------------------- 1 | import re 2 | import time 3 | 4 | from urllib.request import urlopen 5 | 6 | import gspread 7 | 8 | from oauth2client.service_account import ServiceAccountCredentials 9 | from bs4 import BeautifulSoup 10 | 11 | coolpc_url = 'http://www.coolpc.com.tw/evaluate.php' 12 | ram_text = '記憶體 RAM' 13 | 14 | auth_json_path = 'auth.json' 15 | 16 | gss_scopes = ['https://spreadsheets.google.com/feeds'] 17 | spreadsheet_key_path = 'spreadsheet_key' 18 | 19 | 20 | def auth_gss_client(path, scopes): 21 | credentials = ServiceAccountCredentials.from_json_keyfile_name(path, 22 | scopes) 23 | return gspread.authorize(credentials) 24 | 25 | 26 | def get_cheapest(url, text): 27 | with urlopen(url) as response: 28 | soup = BeautifulSoup(response.read(), 'lxml') 29 | 30 | cheapest_price = cheapest_item = None 31 | 32 | re_price = re.compile(r'\$(\d+)') 33 | root = soup.find('td', text=re.compile(text)).parent 34 | 35 | for option in root.find_all('option', text=re_price): 36 | item = option.text.strip() 37 | price = int(re_price.search(item).group(1)) 38 | if cheapest_price is None or price < cheapest_price: 39 | cheapest_price = price 40 | cheapest_item = item 41 | 42 | return (cheapest_item, cheapest_price) 43 | 44 | 45 | def update_sheet(gss_client, key, today, item, price): 46 | wks = gss_client.open_by_key(key) 47 | sheet = wks.sheet1 48 | sheet.insert_row([today, item, price], 2) 49 | 50 | 51 | if __name__ == '__main__': 52 | (cheapest_item, cheapest_price) = get_cheapest(coolpc_url, ram_text) 53 | if cheapest_price is not None: 54 | today = time.strftime("%c") 55 | gss_client = auth_gss_client(auth_json_path, gss_scopes) 56 | with open(spreadsheet_key_path) as f: 57 | spreadsheet_key = f.read().strip() 58 | update_sheet(gss_client, spreadsheet_key, today, cheapest_item, 59 | cheapest_price) 60 | -------------------------------------------------------------------------------- /ipynb/requirements.txt: -------------------------------------------------------------------------------- 1 | cairocffi>=0.7.2 2 | ipython>=1.2.1 3 | matplotlib>=1.5.0 4 | mpld3>=0.2 5 | numpy>=1.8.2 6 | pandas>=0.17.1 7 | scikit-learn>=0.16.1 8 | scipy>=0.13.3 9 | wordcloud>=1.2 10 | -------------------------------------------------------------------------------- /plurk_crawler/crawl.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "encoding/json" 5 | "errors" 6 | "flag" 7 | "fmt" 8 | "github.com/schollz/progressbar" 9 | "io/ioutil" 10 | "log" 11 | "net/http" 12 | "net/url" 13 | "os" 14 | "strconv" 15 | "strings" 16 | "time" 17 | ) 18 | 19 | type Plurk struct { 20 | PlurkId int 21 | Content string 22 | Posted string 23 | ResponseCount int 24 | Responses []PlurkResponse 25 | } 26 | 27 | type PlurkResponse struct { 28 | Id int 29 | Handle string 30 | Content string 31 | Posted string 32 | } 33 | 34 | // FetchResponses fetches responses of a given plurk 35 | func FetchResponses(client *http.Client, plurkId int) ([]PlurkResponse, error) { 36 | const fetchUrl = "https://www.plurk.com/Responses/get2" 37 | 38 | data := url.Values{} 39 | data.Set("plurk_id", strconv.Itoa(plurkId)) 40 | data.Set("from_response", "0") 41 | req, _ := http.NewRequest("POST", fetchUrl, strings.NewReader(data.Encode())) 42 | req.Header.Set("X-Requested-With", "XMLHttpRequest") 43 | req.Header.Set("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8") 44 | 45 | // fetch responses 46 | resp, err := client.Do(req) 47 | if err != nil || resp.StatusCode != 200 { 48 | return []PlurkResponse{}, errors.New("connot load") 49 | } 50 | defer resp.Body.Close() 51 | 52 | content, err := ioutil.ReadAll(resp.Body) 53 | if err != nil { 54 | return []PlurkResponse{}, errors.New("connot load") 55 | } 56 | 57 | result := ParseJson(content) 58 | var responses []PlurkResponse 59 | for _, value := range result["responses"].([]interface{}) { 60 | switch obj := value.(type) { 61 | case map[string]interface{}: 62 | response := PlurkResponse{ 63 | Id: int(obj["id"].(float64)), 64 | Handle: obj["handle"].(string), 65 | Content: obj["content"].(string), 66 | Posted: obj["posted"].(string), 67 | } 68 | responses = append(responses, response) 69 | default: 70 | } 71 | } 72 | return responses, nil 73 | } 74 | 75 | // FetchAndSavePlurks queries plurk.com to 76 | // obtain responsese from each plurk and 77 | // save them to disk 78 | func FetchAndSavePlurks(outputDir string, plurks []Plurk, delay int) { 79 | client := http.Client{Timeout: time.Second * 15} 80 | bar := progressbar.New(len(plurks)) 81 | for _, plurk := range plurks { 82 | var responses []PlurkResponse 83 | var err error 84 | if plurk.ResponseCount > 0 { 85 | responses, err = FetchResponses(&client, plurk.PlurkId) 86 | } 87 | if err != nil { 88 | log.Printf("[WARN] Responses from %d cannot be fetched\n", plurk.PlurkId) 89 | } else { 90 | plurk.Responses = responses 91 | plurkJson, _ := json.Marshal(plurk) 92 | ioutil.WriteFile(fmt.Sprintf("%s/%d.json", outputDir, plurk.PlurkId), plurkJson, 0644) 93 | } 94 | bar.Add(1) 95 | if delay > 0 { 96 | time.Sleep(time.Millisecond * time.Duration(delay)) 97 | } 98 | } 99 | } 100 | 101 | // ProcessPlurks parses the timeline, 102 | // and produces a list of plurks. 103 | func ProcessPlurks(result map[string]interface{}) []Plurk { 104 | var plurks []Plurk 105 | for _, value := range result { 106 | // Each value is an interface{} type, that is type asserted as a string 107 | switch obj := value.(type) { 108 | case map[string]interface{}: 109 | plurk := Plurk{ 110 | PlurkId: int(obj["plurk_id"].(float64)), 111 | Content: obj["content"].(string), 112 | Posted: obj["posted"].(string), 113 | ResponseCount: int(obj["response_count"].(float64))} 114 | plurks = append(plurks, plurk) 115 | default: 116 | } 117 | } 118 | 119 | return plurks 120 | } 121 | 122 | func ParseJson(inputs []byte) map[string]interface{} { 123 | var result map[string]interface{} 124 | json.Unmarshal(inputs, &result) 125 | 126 | return result 127 | } 128 | 129 | // GetPlurksFromFile loads json file 130 | // to obtain plurk timeline. 131 | func GetPlurksFromFile(path string) map[string]interface{} { 132 | file, err := os.Open(path) 133 | if err != nil { 134 | log.Fatal(err) 135 | } 136 | defer file.Close() 137 | 138 | content, err := ioutil.ReadAll(file) 139 | if err != nil { 140 | log.Fatal(err) 141 | } 142 | 143 | return ParseJson(content) 144 | } 145 | 146 | // GetPlurksFromFile queris uri 147 | // to obtain plurk timeline. 148 | func GetPlurks(uri string) map[string]interface{} { 149 | client := http.Client{Timeout: time.Second * 15} 150 | resp, err := client.Get(uri) 151 | if err != nil { 152 | log.Fatal(err) 153 | } 154 | defer resp.Body.Close() 155 | 156 | content, err := ioutil.ReadAll(resp.Body) 157 | if err != nil { 158 | log.Fatal(err) 159 | } 160 | 161 | return ParseJson(content) 162 | } 163 | 164 | func main() { 165 | const startUrl = "https://www.plurk.com/Stats/getAnonymousPlurks?lang=%s" 166 | 167 | // parse args 168 | var lang = flag.String("lang", "zh", "language of Plurks") 169 | var outputDir = flag.String("output-dir", "output", "directory for output") 170 | var file = flag.String("file", "", "read file instead of query URL") 171 | var delay = flag.Int("delay", 1000, "delay between each request in milliseconds") 172 | flag.Parse() 173 | 174 | // get plurks 175 | var result map[string]interface{} 176 | if *file != "" { 177 | result = GetPlurksFromFile(*file) 178 | } else { 179 | uri := fmt.Sprintf(startUrl, *lang) 180 | result = GetPlurks(uri) 181 | } 182 | 183 | plurks := ProcessPlurks(result) 184 | 185 | // start storing content... 186 | os.MkdirAll(*outputDir, 0700) 187 | FetchAndSavePlurks(*outputDir, plurks, *delay) 188 | } 189 | -------------------------------------------------------------------------------- /sacred/Sampled MNIST.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": 1, 6 | "metadata": { 7 | "collapsed": false 8 | }, 9 | "outputs": [], 10 | "source": [ 11 | "%matplotlib inline" 12 | ] 13 | }, 14 | { 15 | "cell_type": "code", 16 | "execution_count": 2, 17 | "metadata": { 18 | "collapsed": true 19 | }, 20 | "outputs": [], 21 | "source": [ 22 | "import matplotlib.pyplot as plt\n", 23 | "import numpy as np\n", 24 | "\n", 25 | "from pymongo import MongoClient" 26 | ] 27 | }, 28 | { 29 | "cell_type": "code", 30 | "execution_count": 3, 31 | "metadata": { 32 | "collapsed": true 33 | }, 34 | "outputs": [], 35 | "source": [ 36 | "sample_sizes = (500, 1000, 1500)\n", 37 | "classifiers = ('svc', 'xgb', 'softmax')" 38 | ] 39 | }, 40 | { 41 | "cell_type": "code", 42 | "execution_count": 4, 43 | "metadata": { 44 | "collapsed": true 45 | }, 46 | "outputs": [], 47 | "source": [ 48 | "mc = MongoClient()\n", 49 | "db = mc['sacred']" 50 | ] 51 | }, 52 | { 53 | "cell_type": "code", 54 | "execution_count": 5, 55 | "metadata": { 56 | "collapsed": false 57 | }, 58 | "outputs": [], 59 | "source": [ 60 | "results = []\n", 61 | "for clf in classifiers:\n", 62 | " clf_mean = []\n", 63 | " clf_std = []\n", 64 | " for sample_size in sample_sizes:\n", 65 | " test_accuracy = []\n", 66 | " for seed in range(1, 11):\n", 67 | " result = db['runs'].find_one({'experiment.name': 'mnist_sampled',\n", 68 | " 'config.classifier': clf,\n", 69 | " 'config.sample_size': sample_size,\n", 70 | " 'config.seed': seed,\n", 71 | " 'status': 'COMPLETED'},\n", 72 | " {'result': 1})\n", 73 | " test_accuracy.append(result['result']['test_accuracy'])\n", 74 | " clf_mean.append(np.mean(test_accuracy))\n", 75 | " clf_std.append(np.std(test_accuracy))\n", 76 | " results.append((clf_mean, clf_std))" 77 | ] 78 | }, 79 | { 80 | "cell_type": "code", 81 | "execution_count": 6, 82 | "metadata": { 83 | "collapsed": false 84 | }, 85 | "outputs": [ 86 | { 87 | "data": { 88 | "image/png": "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\nfeBN8zec8opZX4n3sL/aOXeppBslzTCzayOf9D5h8XUFoBH6BnBKT0i6WNKlkvZJWhDb5sR52Dvn\nKr3fByT9XuFp+f3elIq83we84pWS+kdU7+ftA+JFa/tGpbfdeD/ga865/c65eudcSNIz+uKUb8z6\nStyGvZmdb2bJDduSrpe0XdIqSVO8YlMkvextr5J0m5mdZ2aDFF5AsfnsthqIqVb1DW/Kv8bMrvBW\nFv9zRB3Atxo+FHsmKpwtUgz7SmJ7HqyDyZD0e+/bDYmS/sM5t8bM3pW03MzuVPgOerdKknNuh5kt\nl1QmKShphnOuPjZNB6LLzJZIGiupp5l9Iumnkv5Vre8b9ym8WrmLpP/2fgDfaKavjDWzSxU+1VUh\n6XtSbPsKV9ADAMDn4nYaHwCAeEHYAwDgc4Q9AAA+R9gDAOBzhD0AAD5H2ANoMzNbb2YjWy55ymPc\nbGaz2qtNAL4snr9nD+Ac4JxbpfDFRgBECSN7wEe8K0O+YmalZrbdzP7R2/+omb3r7Xu64V7Z3sj8\nV2b2npl9aGaXm9lK7571v/DKDDSznWb2O69MsZl1beK1rzezd8xsi5n9p5l1a6LMA2ZW5t0gZKm3\nb6qZFXnbWyN+jpvZGO89PW9mm83sfTO7JZp/Q8CPCHvAX26QVOWcy3PODZW0xttf5Jy73NvXRdKE\niDonnXMjJT2p8CU6Z0gaKmmqmaV5ZbIlLXLOXSKpRuGrff2NmfWU9BNJX3PO5Ut6T9LMJto3S9Jl\nzrnhkqY3frLh/t+S5njHeFvSbElvOOdGSRonab53iWsAp4mwB/xlm6TrzOyXZnaNc+4zb/84M9tk\nZtskfVXSkIg6qyLq7nDO7XPOnZC0W1/ctGOvc+4tb/u3kq5u9LpXSMqV9JaZbVX42vkXNdG+DyT9\nzszuUPhyoV9iZoMlzZd0q3OuTuH7VszyjrteUmdJA1r6QwD4AufsAR9xzu0ys3xJN0n6hZmtlfSY\npEWSRjrn9prZzxQOzAYnvN+hiO2Gxw3/RzS+rnbjxybpj865ghaa+A+SrpX0DUmzzWzY3x0kPPW/\nXNLd3s1BGo79befcRy0cG0AzGNkDPmJmfSTVOud+q/DoOF9fBPshL0wnteHQA8xstLd9u6Q3Gz3/\nJ0lXmVmm147zzSyrUdsCkvo759ZJ+pGkFEmNz+s/L+kF59zGiH1/kHR/xDqDy9rQfiCuMbIH/GWY\n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\nAAAAAElFTkSuQmCC\n", 89 | "text/plain": [ 90 | "" 91 | ] 92 | }, 93 | "metadata": {}, 94 | "output_type": "display_data" 95 | } 96 | ], 97 | "source": [ 98 | "fig, ax = plt.subplots(nrows=1, ncols=1, figsize=(8, 6))\n", 99 | "\n", 100 | "ax.set_title('Test Accuracy of Different Classifiers on MNIST')\n", 101 | "ax.set_xlabel('sample size')\n", 102 | "ax.set_ylabel('test accuracy')\n", 103 | "\n", 104 | "xticks = np.arange(len(sample_sizes))\n", 105 | "ax.set_xticks(xticks)\n", 106 | "ax.set_xticklabels(sample_sizes)\n", 107 | "\n", 108 | "for i, clf in enumerate(classifiers):\n", 109 | " ax.errorbar(xticks, results[i][0], yerr=results[i][1], label=clf)\n", 110 | "ax.legend(bbox_to_anchor=(1, -0.1))\n", 111 | " \n", 112 | "plt.show()" 113 | ] 114 | } 115 | ], 116 | "metadata": { 117 | "kernelspec": { 118 | "display_name": "Python 3", 119 | "language": "python", 120 | "name": "python3" 121 | }, 122 | "language_info": { 123 | "codemirror_mode": { 124 | "name": "ipython", 125 | "version": 3 126 | }, 127 | "file_extension": ".py", 128 | "mimetype": "text/x-python", 129 | "name": "python", 130 | "nbconvert_exporter": "python", 131 | "pygments_lexer": "ipython3", 132 | "version": "3.6.0" 133 | } 134 | }, 135 | "nbformat": 4, 136 | "nbformat_minor": 2 137 | } 138 | -------------------------------------------------------------------------------- /sacred/mnist.py: -------------------------------------------------------------------------------- 1 | """ 2 | Sampled MNIST Experiments 3 | """ 4 | 5 | import numpy as np 6 | import xgboost as xgb 7 | 8 | from sacred import Experiment 9 | from sklearn.metrics import accuracy_score 10 | from sklearn.svm import LinearSVC 11 | from tensorflow.examples.tutorials.mnist import input_data 12 | 13 | from softmax import SoftmaxClassifier 14 | 15 | ex = Experiment('mnist_sampled') 16 | 17 | 18 | @ex.config 19 | def my_config(): 20 | sample_size = 500 21 | classifier = 'svc' 22 | 23 | 24 | @ex.capture 25 | def sample_data(X, y, sample_size, _rnd): 26 | indices = np.arange(X.shape[0]) 27 | choice_indices = _rnd.choice(indices, sample_size) 28 | return X[choice_indices, :], y[choice_indices] 29 | 30 | 31 | @ex.capture 32 | def get_classifier(classifier): 33 | if classifier == 'svc': 34 | return LinearSVC() 35 | elif classifier == 'softmax': 36 | return SoftmaxClassifier() 37 | elif classifier == 'xgb': 38 | return xgb.XGBClassifier() 39 | else: 40 | return None 41 | 42 | 43 | @ex.automain 44 | def run_experiments(data_dir='MNIST_data'): 45 | mnist = input_data.read_data_sets(data_dir, one_hot=False) 46 | X_test = mnist.test.images 47 | y_test = mnist.test.labels 48 | 49 | X_train, y_train = sample_data(mnist.train.images, mnist.train.labels) 50 | 51 | clf = get_classifier() 52 | clf.fit(X_train, y_train) 53 | y_train_pred = clf.predict(X_train) 54 | train_accuracy = accuracy_score(y_train, y_train_pred) 55 | 56 | y_test_pred = clf.predict(X_test) 57 | test_accuracy = accuracy_score(y_test, y_test_pred) 58 | 59 | return {'train_accuracy': train_accuracy, 'test_accuracy': test_accuracy} 60 | -------------------------------------------------------------------------------- /sacred/requirements.txt: -------------------------------------------------------------------------------- 1 | jupyter==1.0.0 2 | matplotlib==2.0.0 3 | notebook==5.7.8 4 | numpy==1.11.3 5 | pymongo==3.4.0 6 | sacred==0.7b3 7 | sacredboard==0.1.2 8 | scikit-learn==0.18.1 9 | tensorflow-gpu==1.0.1 10 | xgboost==0.6a2 11 | -------------------------------------------------------------------------------- /sacred/run.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | for clf in svc xgb softmax; 4 | do 5 | for sample_size in 500 1000 1500; 6 | do 7 | for seed in 1 2 3 4 5 6 7 8 9 10; 8 | do 9 | python mnist.py with classifier=$clf sample_size=$sample_size seed=$seed -m sacred || exit 1 10 | done 11 | done 12 | done 13 | -------------------------------------------------------------------------------- /sacred/softmax.py: -------------------------------------------------------------------------------- 1 | """ 2 | Softmax classifier implemented with TensorFlow 3 | """ 4 | 5 | import tensorflow as tf 6 | 7 | from tensorflow.contrib import layers 8 | from tensorflow.contrib.learn.python.learn.estimators import estimator 9 | from tensorflow.contrib.learn.python.learn.estimators import model_fn 10 | 11 | 12 | class SoftmaxClassifier(object): 13 | def __init__(self, output_dim=10): 14 | def softmax(features, target, mode): 15 | target = tf.one_hot(target, output_dim, 1, 0) 16 | logits = layers.fully_connected( 17 | inputs=features, 18 | num_outputs=output_dim, 19 | weights_initializer=tf.constant_initializer(0.), 20 | biases_initializer=tf.constant_initializer(0.), 21 | activation_fn=None) 22 | 23 | predictions = tf.argmax(tf.nn.softmax(logits), 1) 24 | loss = tf.reduce_mean( 25 | tf.nn.softmax_cross_entropy_with_logits( 26 | labels=target, logits=logits)) 27 | train_op = tf.contrib.layers.optimize_loss( 28 | loss, 29 | tf.contrib.framework.get_global_step(), 30 | optimizer='Adagrad', 31 | learning_rate=0.5) 32 | 33 | return model_fn.ModelFnOps( 34 | mode=mode, 35 | predictions=predictions, 36 | loss=loss, 37 | train_op=train_op) 38 | 39 | self.clf_ = estimator.SKCompat(estimator.Estimator(model_fn=softmax)) 40 | 41 | def fit(self, X, y, steps=1000, batch_size=50): 42 | self.clf_.fit(X, y, steps=steps, batch_size=batch_size) 43 | return self 44 | 45 | def predict(self, X, batch_size=50): 46 | return self.clf_.predict(X, batch_size=batch_size) 47 | -------------------------------------------------------------------------------- /scrapy/ptt/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shaform/experiments/6b9668220f2f1bf39f2d7f89a8a3092852999f19/scrapy/ptt/__init__.py -------------------------------------------------------------------------------- /scrapy/ptt/items.py: -------------------------------------------------------------------------------- 1 | # Define here the models for your scraped items 2 | # 3 | # See documentation in: 4 | # http://doc.scrapy.org/en/latest/topics/items.html 5 | 6 | import scrapy 7 | 8 | 9 | class PostItem(scrapy.Item): 10 | title = scrapy.Field() 11 | author = scrapy.Field() 12 | date = scrapy.Field() 13 | content = scrapy.Field() 14 | comments = scrapy.Field() 15 | score = scrapy.Field() 16 | url = scrapy.Field() 17 | -------------------------------------------------------------------------------- /scrapy/ptt/pipelines.py: -------------------------------------------------------------------------------- 1 | # Define your item pipelines here 2 | # 3 | # Don't forget to add your pipeline to the ITEM_PIPELINES setting 4 | # See: http://doc.scrapy.org/en/latest/topics/item-pipeline.html 5 | 6 | 7 | class PTTPipeline(object): 8 | def process_item(self, item, spider): 9 | return item 10 | -------------------------------------------------------------------------------- /scrapy/ptt/settings.py: -------------------------------------------------------------------------------- 1 | # Scrapy settings for ptt project 2 | # 3 | # For simplicity, this file contains only settings considered important or 4 | # commonly used. You can find more settings consulting the documentation: 5 | # 6 | # http://doc.scrapy.org/en/latest/topics/settings.html 7 | # http://scrapy.readthedocs.org/en/latest/topics/downloader-middleware.html 8 | # http://scrapy.readthedocs.org/en/latest/topics/spider-middleware.html 9 | 10 | BOT_NAME = 'ptt' 11 | 12 | SPIDER_MODULES = ['ptt.spiders'] 13 | NEWSPIDER_MODULE = 'ptt.spiders' 14 | 15 | # Crawl responsibly by identifying yourself (and your website) on the user-agent 16 | #USER_AGENT = 'ptt (+http://www.yourdomain.com)' 17 | 18 | # Obey robots.txt rules 19 | ROBOTSTXT_OBEY = True 20 | 21 | # Configure maximum concurrent requests performed by Scrapy (default: 16) 22 | #CONCURRENT_REQUESTS = 32 23 | 24 | # Configure a delay for requests for the same website (default: 0) 25 | # See http://scrapy.readthedocs.org/en/latest/topics/settings.html#download-delay 26 | # See also autothrottle settings and docs 27 | DOWNLOAD_DELAY = 1.25 28 | # The download delay setting will honor only one of: 29 | #CONCURRENT_REQUESTS_PER_DOMAIN = 16 30 | #CONCURRENT_REQUESTS_PER_IP = 16 31 | 32 | # Disable cookies (enabled by default) 33 | #COOKIES_ENABLED = False 34 | 35 | # Disable Telnet Console (enabled by default) 36 | #TELNETCONSOLE_ENABLED = False 37 | 38 | # Override the default request headers: 39 | #DEFAULT_REQUEST_HEADERS = { 40 | # 'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8', 41 | # 'Accept-Language': 'en', 42 | #} 43 | 44 | # Enable or disable spider middlewares 45 | # See http://scrapy.readthedocs.org/en/latest/topics/spider-middleware.html 46 | #SPIDER_MIDDLEWARES = { 47 | # 'ptt.middlewares.MyCustomSpiderMiddleware': 543, 48 | #} 49 | 50 | # Enable or disable downloader middlewares 51 | # See http://scrapy.readthedocs.org/en/latest/topics/downloader-middleware.html 52 | #DOWNLOADER_MIDDLEWARES = { 53 | # 'ptt.middlewares.MyCustomDownloaderMiddleware': 543, 54 | #} 55 | 56 | # Enable or disable extensions 57 | # See http://scrapy.readthedocs.org/en/latest/topics/extensions.html 58 | #EXTENSIONS = { 59 | # 'scrapy.extensions.telnet.TelnetConsole': None, 60 | #} 61 | 62 | # Configure item pipelines 63 | # See http://scrapy.readthedocs.org/en/latest/topics/item-pipeline.html 64 | #ITEM_PIPELINES = { 65 | # 'ptt.pipelines.SomePipeline': 300, 66 | #} 67 | 68 | # Enable and configure the AutoThrottle extension (disabled by default) 69 | # See http://doc.scrapy.org/en/latest/topics/autothrottle.html 70 | #AUTOTHROTTLE_ENABLED = True 71 | # The initial download delay 72 | #AUTOTHROTTLE_START_DELAY = 5 73 | # The maximum download delay to be set in case of high latencies 74 | #AUTOTHROTTLE_MAX_DELAY = 60 75 | # The average number of requests Scrapy should be sending in parallel to 76 | # each remote server 77 | #AUTOTHROTTLE_TARGET_CONCURRENCY = 1.0 78 | # Enable showing throttling stats for every response received: 79 | #AUTOTHROTTLE_DEBUG = False 80 | 81 | # Enable and configure HTTP caching (disabled by default) 82 | # See http://scrapy.readthedocs.org/en/latest/topics/downloader-middleware.html#httpcache-middleware-settings 83 | #HTTPCACHE_ENABLED = True 84 | #HTTPCACHE_EXPIRATION_SECS = 0 85 | #HTTPCACHE_DIR = 'httpcache' 86 | #HTTPCACHE_IGNORE_HTTP_CODES = [] 87 | #HTTPCACHE_STORAGE = 'scrapy.extensions.httpcache.FilesystemCacheStorage' 88 | -------------------------------------------------------------------------------- /scrapy/ptt/spiders/__init__.py: -------------------------------------------------------------------------------- 1 | # This package will contain the spiders of your Scrapy project 2 | # 3 | # Please refer to the documentation for information on how to create and manage 4 | # your spiders. 5 | -------------------------------------------------------------------------------- /scrapy/ptt/spiders/ptt.py: -------------------------------------------------------------------------------- 1 | import logging 2 | 3 | from datetime import datetime 4 | 5 | import html2text 6 | import scrapy 7 | 8 | from scrapy.http import FormRequest 9 | 10 | from ptt.items import PostItem 11 | 12 | 13 | class PTTSpider(scrapy.Spider): 14 | name = 'ptt' 15 | allowed_domains = ['ptt.cc'] 16 | start_urls = ('https://www.ptt.cc/bbs/Gossiping/index.html', ) 17 | 18 | _retries = 0 19 | MAX_RETRY = 1 20 | 21 | _pages = 0 22 | MAX_PAGES = 2 23 | 24 | def parse(self, response): 25 | if len(response.xpath('//div[@class="over18-notice"]')) > 0: 26 | if self._retries < PTTSpider.MAX_RETRY: 27 | self._retries += 1 28 | logging.warning('retry {} times...'.format(self._retries)) 29 | yield FormRequest.from_response(response, 30 | formdata={'yes': 'yes'}, 31 | callback=self.parse) 32 | else: 33 | logging.warning('you cannot pass') 34 | 35 | else: 36 | self._pages += 1 37 | for href in response.css('.r-ent > div.title > a::attr(href)'): 38 | url = response.urljoin(href.extract()) 39 | yield scrapy.Request(url, callback=self.parse_post) 40 | 41 | if self._pages < PTTSpider.MAX_PAGES: 42 | next_page = response.xpath( 43 | '//div[@id="action-bar-container"]//a[contains(text(), "上頁")]/@href') 44 | if next_page: 45 | url = response.urljoin(next_page[0].extract()) 46 | logging.warning('follow {}'.format(url)) 47 | yield scrapy.Request(url, self.parse) 48 | else: 49 | logging.warning('no next page') 50 | else: 51 | logging.warning('max pages reached') 52 | 53 | def parse_post(self, response): 54 | item = PostItem() 55 | item['title'] = response.xpath( 56 | '//meta[@property="og:title"]/@content')[0].extract() 57 | item['author'] = response.xpath( 58 | '//div[@class="article-metaline"]/span[text()="作者"]/following-sibling::span[1]/text()')[ 59 | 0].extract().split(' ')[0] 60 | datetime_str = response.xpath( 61 | '//div[@class="article-metaline"]/span[text()="時間"]/following-sibling::span[1]/text()')[ 62 | 0].extract() 63 | item['date'] = datetime.strptime(datetime_str, '%a %b %d %H:%M:%S %Y') 64 | 65 | converter = html2text.HTML2Text() 66 | converter.ignore_links = True 67 | item['content'] = converter.handle(response.xpath('//div[@id="main-content"]')[ 0].extract()) 68 | 69 | comments = [] 70 | total_score = 0 71 | for comment in response.xpath('//div[@class="push"]'): 72 | push_tag = comment.css('span.push-tag::text')[0].extract() 73 | push_user = comment.css('span.push-userid::text')[0].extract() 74 | push_content = comment.css('span.push-content::text')[0].extract() 75 | 76 | if '推' in push_tag: 77 | score = 1 78 | elif '噓' in push_tag: 79 | score = -1 80 | else: 81 | score = 0 82 | 83 | total_score += score 84 | 85 | comments.append({'user': push_user, 86 | 'content': push_content, 87 | 'score': score}) 88 | 89 | item['comments'] = comments 90 | item['score'] = total_score 91 | item['url'] = response.url 92 | 93 | yield item 94 | -------------------------------------------------------------------------------- /scrapy/requirements.txt: -------------------------------------------------------------------------------- 1 | Scrapy>=1.1.0 2 | jieba>=0.38 3 | seaborn>=0.7.0 4 | numpy>=1.10.4 5 | scipy>=0.17.0 6 | scikit-learn>=0.16.1 7 | cryptography>=1.2.2 8 | pyOpenSSL>=0.15.1 9 | html2text>=2016.5.29 10 | -------------------------------------------------------------------------------- /scrapy/scrapy.cfg: -------------------------------------------------------------------------------- 1 | # Automatically created by: scrapy startproject 2 | # 3 | # For more information about the [deploy] section see: 4 | # https://scrapyd.readthedocs.org/en/latest/deploy.html 5 | 6 | [settings] 7 | default = ptt.settings 8 | 9 | [deploy] 10 | #url = http://localhost:6800/ 11 | project = ptt 12 | -------------------------------------------------------------------------------- /sentiment_analysis/evaluate.py: -------------------------------------------------------------------------------- 1 | import argparse 2 | import os 3 | 4 | 5 | def process_commands(): 6 | parser = argparse.ArgumentParser() 7 | parser.add_argument('--scores', required=True) 8 | parser.add_argument('--test_pos', required=True) 9 | 10 | return parser.parse_args() 11 | 12 | 13 | if __name__ == '__main__': 14 | args = process_commands() 15 | 16 | with open(args.test_pos, 'r') as pos: 17 | plen = len([1 for l in pos]) 18 | 19 | with open(args.scores, 'r') as s: 20 | correct = total = 0 21 | for i, v in enumerate(s): 22 | if i < plen: 23 | if float(v) < 0: 24 | correct += 1 25 | else: 26 | if float(v) > 0: 27 | correct += 1 28 | total += 1 29 | print('{} accuracy: {:.4f}%'.format( 30 | os.path.basename(args.scores), correct / total * 100)) 31 | -------------------------------------------------------------------------------- /sentiment_analysis/normalize.py: -------------------------------------------------------------------------------- 1 | import argparse 2 | import math 3 | 4 | 5 | def process_commands(): 6 | parser = argparse.ArgumentParser() 7 | parser.add_argument('--input', required=True) 8 | parser.add_argument('--output', required=True) 9 | parser.add_argument('--type', required=True, 10 | choices=('rnnlm', 'logreg')) 11 | 12 | return parser.parse_args() 13 | 14 | 15 | def load_data(path): 16 | with open(path, 'r') as f: 17 | return [float(l) for l in f] 18 | 19 | 20 | def save_data(path, data): 21 | with open(path, 'w') as f: 22 | for n in data: 23 | f.write('{}\n'.format(n)) 24 | 25 | 26 | def norm(data): 27 | mean = math.sqrt(sum(n * n for n in data)) 28 | return [n / mean for n in data] 29 | 30 | if __name__ == '__main__': 31 | args = process_commands() 32 | 33 | data = load_data(args.input) 34 | 35 | if args.type == 'rnnlm': 36 | data = [n - 1 for n in data] 37 | elif args.type == 'logreg': 38 | data = [n - 0.5 for n in data] 39 | else: 40 | raise 41 | 42 | data = norm(data) 43 | 44 | save_data(args.output, data) 45 | -------------------------------------------------------------------------------- /sentiment_analysis/preprocess.py: -------------------------------------------------------------------------------- 1 | import argparse 2 | import re 3 | import socket 4 | 5 | 6 | def seg_it(q_str, *, server, port, user, password, pos=False): 7 | sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 8 | sock.connect((server, port)) 9 | query = ('' + 10 | '' + 11 | '') 14 | sock.send(query.encode(encoding='utf-8')) 15 | result = sock.recv(1048576).decode('utf-8', errors='ignore') 16 | sock.close() 17 | try: 18 | stripped = result.replace('', '').split( 19 | '')[1].split('')[0] 20 | except: 21 | print(q_str) 22 | print(result) 23 | raise 24 | if pos: 25 | return stripped.replace('\u3000', ' ').strip() 26 | else: 27 | return re.sub(r'\([^) ]+\)(\u3000|$)', ' ', stripped).strip() 28 | 29 | 30 | def process_commands(): 31 | parser = argparse.ArgumentParser() 32 | parser.add_argument('--server', required=True) 33 | parser.add_argument('--port', type=int, required=True) 34 | parser.add_argument('--user', required=True) 35 | parser.add_argument('--password', required=True) 36 | parser.add_argument('--input', required=True) 37 | parser.add_argument('--output', required=True) 38 | 39 | return parser.parse_args() 40 | 41 | if __name__ == '__main__': 42 | args = process_commands() 43 | 44 | data_set = set() 45 | with open(args.input, 'r') as f: 46 | while True: 47 | cls = f.readline() 48 | line = f.readline() 49 | 50 | if not line: 51 | break 52 | 53 | data_set.add((int(cls), line.split('|', 1)[1].strip())) 54 | 55 | with open(args.output, 'w') as f: 56 | for i, (cls, text) in enumerate(data_set): 57 | try: 58 | f.write('{} {}\n'.format(cls, 59 | seg_it(text, 60 | server=args.server, 61 | port=args.port, 62 | user=args.user, 63 | password=args.password))) 64 | except IndexError: 65 | pass 66 | if i % 100 == 99: 67 | print('{} processed'.format(i + 1)) 68 | -------------------------------------------------------------------------------- /sentiment_analysis/run.sh: -------------------------------------------------------------------------------- 1 | # -- preprocess -- # 2 | # You'll need 207884_hotel_training.txt & CKIP segmenter to run preprocess.py 3 | #python3 preprocess.py --server $SERVER --port $PORT --user $USER --password $PASS --output data.txt --input 207884_hotel_training.txt 4 | python3 split.py --input data/data.txt --train_pos data/train_pos.txt --train_neg data/train_neg.txt --test_pos data/test_pos.txt --test_neg data/test_neg.txt 5 | 6 | # -- rnnlm -- # 7 | mkdir rnnlm 8 | cd rnnlm 9 | wget https://f25ea9ccb7d3346ce6891573d543960492b92c30.googledrive.com/host/0ByxdPXuxLPS5RFM5dVNvWVhTd0U/rnnlm-0.4b.tgz 10 | tar -xvf rnnlm-0.4b.tgz 11 | g++ -lm -O3 -march=native -Wall -funroll-loops -ffast-math -c rnnlm-0.4b/rnnlmlib.cpp 12 | g++ -lm -O3 -march=native -Wall -funroll-loops -ffast-math rnnlm-0.4b/rnnlm.cpp rnnlmlib.o -o rnnlm 13 | 14 | 15 | # construct positive language model 16 | head -n 200 ../data/train_pos.txt > val.txt 17 | cat ../data/train_pos.txt | sed '1,200d' > train.txt 18 | ./rnnlm -rnnlm pos.model -train train.txt -valid val.txt -hidden 50 -direct-order 3 -direct 200 -class 100 -debug 2 -bptt 4 -bptt-block 10 -binary 19 | 20 | # construct negative language model 21 | head -n 200 ../data/train_neg.txt > val.txt 22 | cat ../data/train_neg.txt | sed '1,200d' > train.txt 23 | ./rnnlm -rnnlm neg.model -train train.txt -valid val.txt -hidden 50 -direct-order 3 -direct 200 -class 100 -debug 2 -bptt 4 -bptt-block 10 -binary 24 | 25 | cat ../data/test_pos.txt ../data/test_neg.txt | nl -v0 -s' ' -w1 > test.txt 26 | ./rnnlm -rnnlm pos.model -test test.txt -debug 0 -nbest > model_pos_score.txt 27 | ./rnnlm -rnnlm neg.model -test test.txt -debug 0 -nbest > model_neg_score.txt 28 | 29 | mkdir ../scores 30 | paste model_pos_score.txt model_neg_score.txt | awk '{print $1/$2;}' > ../scores/RNNLM 31 | cd .. 32 | python3 normalize.py --input scores/RNNLM --output scores/RNNLM --type rnnlm 33 | 34 | python3 evaluate.py --test_pos data/test_pos.txt --scores scores/RNNLM 35 | 36 | # -- word2vec - sentence vectors -- # 37 | git clone https://github.com/shaform/word2vec.git 38 | cd word2vec 39 | git checkout doc2vec 40 | make 41 | 42 | cat ../data/train_pos.txt ../data/train_neg.txt ../data/test_pos.txt ../data/test_neg.txt | nl -v0 -s' ' -w1 | sed 's/^/@@SS-/' | shuf > all.txt 43 | time ./word2vec -train all.txt -output vectors.txt -cbow 0 -size 400 -window 10 -negative 5 -hs 1 -sample 1e-3 -threads 24 -binary 0 -iter 20 -min-count 1 -sentence-vectors 1 44 | grep '@@SS-' vectors.txt | sed -e 's/^@@SS-//' | sort -n > sentence_vectors.txt 45 | 46 | mkdir ../liblinear 47 | cd ../liblinear 48 | wget -O liblinear.zip http://www.csie.ntu.edu.tw/~cjlin/cgi-bin/liblinear.cgi?+http://www.csie.ntu.edu.tw/~cjlin/liblinear+zip 49 | unzip liblinear.zip 50 | cd * 51 | make 52 | cp train predict .. 53 | cd ../../word2vec 54 | 55 | python3 ../transform.py --input sentence_vectors.txt --output sentence_features.txt 56 | python3 ../train.py --features sentence_features.txt --train_pos ../data/train_pos.txt --train_neg ../data/train_neg.txt --test_pos ../data/test_pos.txt --output_train train.txt --output_test test.txt 57 | 58 | ../liblinear/train -s 0 train.txt model.logreg 59 | ../liblinear/predict -b 1 test.txt model.logreg out.logreg 60 | 61 | sed '1d' out.logreg | cut -d' ' -f3 > ../scores/DOC2VEC 62 | cd .. 63 | python3 normalize.py --input scores/DOC2VEC --output scores/DOC2VEC --type logreg 64 | 65 | python3 evaluate.py --test_pos data/test_pos.txt --scores scores/DOC2VEC 66 | 67 | # -- TF-IDF -- # 68 | mkdir tfidf 69 | cd tfidf 70 | cat ../data/train_pos.txt ../data/train_neg.txt ../data/test_pos.txt ../data/test_neg.txt > all.txt 71 | python3 ../tfidf.py --input all.txt --output features.txt 72 | python3 ../train.py --features features.txt --train_pos ../data/train_pos.txt --train_neg ../data/train_neg.txt --test_pos ../data/test_pos.txt --output_train train.txt --output_test test.txt 73 | 74 | ../liblinear/train -s 0 train.txt model.logreg 75 | ../liblinear/predict -b 1 test.txt model.logreg out.logreg 76 | 77 | sed '1d' out.logreg | cut -d' ' -f3 > ../scores/TFIDF 78 | cd .. 79 | python3 normalize.py --input scores/TFIDF --output scores/TFIDF --type logreg 80 | 81 | python3 evaluate.py --test_pos data/test_pos.txt --scores scores/TFIDF 82 | 83 | # -- TOTAL -- # 84 | 85 | paste scores/RNNLM scores/DOC2VEC scores/TFIDF | awk '{print ($1+$2+$3)/3;}' > scores/TOTAL 86 | python3 evaluate.py --test_pos data/test_pos.txt --scores scores/TOTAL 87 | -------------------------------------------------------------------------------- /sentiment_analysis/split.py: -------------------------------------------------------------------------------- 1 | import argparse 2 | import random 3 | 4 | 5 | def process_commands(): 6 | parser = argparse.ArgumentParser() 7 | parser.add_argument('--input', required=True) 8 | parser.add_argument('--train_pos', required=True) 9 | parser.add_argument('--train_neg', required=True) 10 | parser.add_argument('--test_pos', required=True) 11 | parser.add_argument('--test_neg', required=True) 12 | 13 | return parser.parse_args() 14 | 15 | 16 | def output(path, sents): 17 | with open(path, 'w') as f: 18 | f.writelines(sents) 19 | 20 | if __name__ == '__main__': 21 | args = process_commands() 22 | 23 | pos = [] 24 | neg = [] 25 | 26 | with open(args.input, 'r') as f: 27 | for l in f: 28 | label, sent = l.split(' ', 1) 29 | if label == '1': 30 | pos.append(sent) 31 | else: 32 | neg.append(sent) 33 | 34 | random.shuffle(pos) 35 | tlen = len(pos) // 10 36 | output(args.test_pos, pos[:tlen]) 37 | output(args.train_pos, pos[tlen:]) 38 | 39 | random.shuffle(neg) 40 | tlen = len(neg) // 10 41 | output(args.test_neg, neg[:tlen]) 42 | output(args.train_neg, neg[tlen:]) 43 | -------------------------------------------------------------------------------- /sentiment_analysis/tfidf.py: -------------------------------------------------------------------------------- 1 | import argparse 2 | import math 3 | 4 | from collections import defaultdict 5 | 6 | 7 | def process_commands(): 8 | parser = argparse.ArgumentParser() 9 | parser.add_argument('--input', required=True) 10 | parser.add_argument('--output', required=True) 11 | 12 | return parser.parse_args() 13 | 14 | 15 | def load_file(path): 16 | with open(path, 'r') as f: 17 | for l in f: 18 | yield l.strip().split() 19 | 20 | 21 | def tf_idf(N, tf, df): 22 | return tf * math.log(N / df) 23 | 24 | if __name__ == '__main__': 25 | args = process_commands() 26 | 27 | d = defaultdict(int) 28 | N = 0 29 | vocab = {} 30 | for tokens in load_file(args.input): 31 | N += 1 32 | for t in set(tokens): 33 | if t not in vocab: 34 | vocab[t] = len(vocab) + 1 35 | d[vocab[t]] += 1 36 | 37 | for t in set(zip(tokens, tokens[1:])): 38 | if t not in vocab: 39 | vocab[t] = len(vocab) + 1 40 | d[vocab[t]] += 1 41 | 42 | with open(args.output, 'w') as f: 43 | for tokens in load_file(args.input): 44 | features = defaultdict(int) 45 | for t in tokens: 46 | features[vocab[t]] += 1 47 | for t in zip(tokens, tokens[1:]): 48 | features[vocab[t]] += 1 49 | 50 | vector = ['{}:{}'.format(k, tf_idf(N, v, d[k])) 51 | for k, v in sorted(features.items())] 52 | f.write('x {}\n'.format(' '.join(vector))) 53 | -------------------------------------------------------------------------------- /sentiment_analysis/train.py: -------------------------------------------------------------------------------- 1 | import argparse 2 | 3 | 4 | def process_commands(): 5 | parser = argparse.ArgumentParser() 6 | parser.add_argument('--features', required=True) 7 | parser.add_argument('--train_pos', required=True) 8 | parser.add_argument('--train_neg', required=True) 9 | parser.add_argument('--test_pos', required=True) 10 | parser.add_argument('--output_train', required=True) 11 | parser.add_argument('--output_test', required=True) 12 | 13 | return parser.parse_args() 14 | 15 | 16 | if __name__ == '__main__': 17 | args = process_commands() 18 | 19 | with open(args.train_pos, 'r') as pos: 20 | plen = len([1 for l in pos]) 21 | 22 | with open(args.train_neg, 'r') as neg: 23 | nlen = len([1 for l in neg]) + plen 24 | 25 | with open(args.test_pos, 'r') as pos: 26 | tplen = len([1 for l in pos]) + nlen 27 | 28 | with open(args.features, 'r') as f, open(args.output_train, 'w') as train, open(args.output_test, 'w') as test: 29 | for i, l in enumerate(f): 30 | _, features = l.split(' ', 1) 31 | if i < plen: 32 | train.write('0 ' + features) 33 | elif i < nlen: 34 | train.write('1 ' + features) 35 | elif i < tplen: 36 | test.write('0 ' + features) 37 | else: 38 | test.write('1 ' + features) 39 | -------------------------------------------------------------------------------- /sentiment_analysis/transform.py: -------------------------------------------------------------------------------- 1 | import argparse 2 | 3 | 4 | def process_commands(): 5 | parser = argparse.ArgumentParser() 6 | parser.add_argument('--input', required=True) 7 | parser.add_argument('--output', required=True) 8 | 9 | return parser.parse_args() 10 | 11 | 12 | if __name__ == '__main__': 13 | args = process_commands() 14 | 15 | with open(args.input, 'r') as ifile, open(args.output, 'w') as ofile: 16 | for l in ifile: 17 | label, *features = l.strip().split() 18 | vector = ['{}:{}'.format(i + 1, v) for i, v in enumerate(features)] 19 | ofile.write('{} {}\n'.format(label, ' '.join(vector))) 20 | -------------------------------------------------------------------------------- /spark_word2vec/get_data.sh: -------------------------------------------------------------------------------- 1 | wget http://mattmahoney.net/dc/text8.zip -O text8.gz 2 | gzip -d text8.gz -f 3 | -------------------------------------------------------------------------------- /spark_word2vec/install.sh: -------------------------------------------------------------------------------- 1 | # install Java 8 2 | sudo add-apt-repository ppa:webupd8team/java 3 | sudo apt-get update 4 | sudo apt-get install oracle-java8-installer 5 | sudo apt-get install oracle-java8-set-default 6 | 7 | # install Apache Spark 8 | wget http://apache.stu.edu.tw/spark/spark-1.4.1/spark-1.4.1-bin-hadoop2.6.tgz 9 | tar -xzf spark-1.4.1-bin-hadoop2.6.tgz 10 | 11 | # install sbt 12 | echo "deb https://dl.bintray.com/sbt/debian /" | sudo tee -a /etc/apt/sources.list.d/sbt.list 13 | sudo apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv 642AC823 14 | sudo apt-get update 15 | sudo apt-get install sbt 16 | -------------------------------------------------------------------------------- /spark_word2vec/run.sh: -------------------------------------------------------------------------------- 1 | cd sparkw2v 2 | sbt package 3 | 4 | cd .. 5 | spark-1.4.1-bin-hadoop2.6/bin/spark-submit --class SparkW2V --master local[*] --executor-memory 20G --driver-memory 10G sparkw2v/target/scala-2.10/spark-word2vec_2.10-1.0.jar 6 | -------------------------------------------------------------------------------- /spark_word2vec/sparkw2v/sparkw2v.sbt: -------------------------------------------------------------------------------- 1 | name := "Spark Word2Vec" 2 | 3 | version := "1.0" 4 | 5 | scalaVersion := "2.10.4" 6 | 7 | libraryDependencies ++= Seq( 8 | "org.apache.spark" %% "spark-core" % "1.4.1" % "provided", 9 | "org.apache.spark" %% "spark-mllib" % "1.4.1" 10 | ) 11 | -------------------------------------------------------------------------------- /spark_word2vec/sparkw2v/src/main/scala/SparkW2V.scala: -------------------------------------------------------------------------------- 1 | import org.apache.spark._ 2 | import org.apache.spark.rdd._ 3 | import org.apache.spark.SparkContext 4 | import org.apache.spark.SparkContext._ 5 | import org.apache.spark.SparkConf 6 | 7 | import org.apache.spark.mllib.feature.Word2Vec 8 | 9 | object SparkW2V { 10 | def main(args: Array[String]) { 11 | val text8 = "{input directory}/text8" 12 | val output = "{output directory}/vectors.txt" 13 | val conf = new SparkConf().setAppName("Spark Word2Vec") 14 | val sc = new SparkContext(conf) 15 | 16 | val input = sc.textFile(text8).map(line => line.split(" ").toSeq) 17 | val word2vec = new Word2Vec() 18 | 19 | val model = word2vec.fit(input) 20 | model.save(sc, output) 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /word2vec_tw/cut.py: -------------------------------------------------------------------------------- 1 | import jieba 2 | import sys 3 | 4 | if __name__ == '__main__': 5 | jieba.set_dictionary('jieba/extra_dict/dict.txt.big') 6 | for l in sys.stdin: 7 | words = jieba.cut(l.strip()) 8 | sys.stdout.write((u' '.join(words) + u'\n').encode('utf8')) 9 | -------------------------------------------------------------------------------- /word2vec_tw/extract_json.py: -------------------------------------------------------------------------------- 1 | import argparse 2 | import json 3 | import re 4 | import sys 5 | 6 | 7 | def yield_moe_sentences(json_obj): 8 | r = re.compile(r'{\[.\*\]}') 9 | for x in yield_sentences(json_obj): 10 | if r.search(x) is None: 11 | yield x 12 | 13 | def yield_sentences(json_obj): 14 | for item in json_obj: 15 | for heteronym in item['heteronyms']: 16 | for definition in heteronym['definitions']: 17 | if 'def' in definition: 18 | sen = definition['def'] 19 | if len(sen) > 0 and '異體字。' not in sen and '<' not in sen: 20 | yield sen 21 | if 'quote' in definition: 22 | yield from definition['quote'] 23 | 24 | if __name__ == '__main__': 25 | moe_json = json.load(sys.stdin) 26 | for x in yield_moe_sentences(moe_json): 27 | sys.stdout.write(x + '\n') 28 | -------------------------------------------------------------------------------- /word2vec_tw/start.sh: -------------------------------------------------------------------------------- 1 | # get moedict data 2 | if [ ! -d moedict-data ]; then 3 | git clone --depth 1 https://github.com/g0v/moedict-data.git 4 | fi 5 | # get json2unicode.pl 6 | if [ ! -d moedict-epub ]; then 7 | git clone --depth 1 https://github.com/g0v/moedict-epub.git 8 | fi 9 | # get jieba segmentation 10 | if [ ! -d jieba ]; then 11 | git clone --depth 1 https://github.com/fxsjy/jieba.git 12 | fi 13 | # get word2vec 14 | if [ ! -d word2vec ]; then 15 | git clone --depth 1 https://github.com/svn2github/word2vec.git 16 | cd word2vec 17 | make 18 | cd .. 19 | fi 20 | # convert to unicode 21 | if [ ! -e dict-revised.unicode.json ]; then 22 | cp -v moedict-data/dict-revised.json moedict-epub/ 23 | cd moedict-epub 24 | perl json2unicode.pl > dict-revised.unicode.json 25 | cp -v dict-revised.unicode.json ../ 26 | cd .. 27 | fi 28 | # extract corpus 29 | if [ ! -e sentences.txt ]; then 30 | python3 extract_json.py < dict-revised.unicode.json > sentences.txt 31 | fi 32 | # segment it 33 | if [ ! -e sentences.segged.txt ]; then 34 | cp -v cut.py jieba/cut.py 35 | python jieba/cut.py < sentences.txt > sentences.segged.txt 36 | fi 37 | # train word vectors 38 | if [ ! -e vectors.bin ]; then 39 | time word2vec/word2vec -train sentences.segged.txt -output vectors.bin -cbow 0 -size 200 -window 10 -negative 5 -hs 0 -sample 1e-4 -threads 24 -binary 1 -iter 20 -min-count 1 40 | fi 41 | -------------------------------------------------------------------------------- /zarr-dataset/clean_anime_faces.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | rm -f anime-faces/aqua_eyes/._danbooru_2559693_dc628b766d7142f2d2d9c75559e36eb5.jpg 3 | rm -f anime-faces/aqua_eyes/._danbooru_2560862_796530ab01cc7bfd8a03c8d05cc6953b.png 4 | -------------------------------------------------------------------------------- /zarr-dataset/convert_anime_faces.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | import numpy as np 4 | import zarr 5 | from torch.utils.data import DataLoader 6 | from torchvision.datasets import ImageFolder 7 | from torch.utils.data import random_split 8 | from tqdm import tqdm 9 | 10 | 11 | def as_array(image): 12 | return np.asarray(image).swapaxes(2, 0) 13 | 14 | 15 | def convert_data_set(path, data_set, batch_size=1000): 16 | loader = DataLoader( 17 | data_set, batch_size=batch_size, shuffle=False, num_workers=4) 18 | 19 | num_examples = len(data_set) 20 | 21 | os.makedirs(path, exist_ok=True) 22 | with zarr.LMDBStore(path) as store: 23 | root = zarr.group(store=store, overwrite=True) 24 | images_set = root.zeros( 25 | 'images', 26 | shape=(num_examples, 3, 96, 96), 27 | chunks=(1, None, None, None), 28 | dtype='u1') 29 | labels_set = root.zeros( 30 | 'labels', shape=(num_examples, ), chunks=(1, ), dtype='u1') 31 | current_iter = 0 32 | for images, labels in tqdm(loader): 33 | size = images.shape[0] 34 | images_set[current_iter:current_iter + size] = images 35 | labels_set[current_iter:current_iter + size] = labels 36 | current_iter += size 37 | 38 | 39 | def main(): 40 | data_set = ImageFolder(root='anime-faces', transform=as_array) 41 | 42 | val_ratio = 0.1 43 | val_size = int(len(data_set) * val_ratio) 44 | train_size = len(data_set) - val_size 45 | 46 | train_set, val_set = random_split(data_set, [train_size, val_size]) 47 | 48 | confs = [ 49 | ('data/anime_faces/train.lmdb', train_set), 50 | ('data/anime_faces/val.lmdb', val_set), 51 | ] 52 | for path, data_set in confs: 53 | convert_data_set(path, data_set) 54 | 55 | 56 | if __name__ == '__main__': 57 | main() 58 | -------------------------------------------------------------------------------- /zarr-dataset/environment.yml: -------------------------------------------------------------------------------- 1 | name: zarr 2 | channels: 3 | - pytorch 4 | - conda-forge 5 | - defaults 6 | dependencies: 7 | - ipython=7.1.1=py36h39e3cac_0 8 | - python-lmdb=0.94=py36h14c3975_0 9 | - python=3.6.7=h0371630_0 10 | - pytorch=0.4.1=py36_py35_py27__9.0.176_7.1.2_2 11 | - torchvision=0.2.1=py36_1 12 | - tqdm=4.28.1=py36h28b3542_0 13 | - zarr=2.2.0=py_1 14 | -------------------------------------------------------------------------------- /zarr-dataset/run.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | ./clean_anime_faces.sh 3 | python convert_anime_faces.py 4 | python test_anime_faces.py 5 | -------------------------------------------------------------------------------- /zarr-dataset/test_anime_faces.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | import zarr 4 | import torch 5 | import torch.nn as nn 6 | import torch.nn.functional as F 7 | from torch.utils.data import DataLoader 8 | from torch.utils.data import Dataset 9 | from tqdm import tqdm, trange 10 | 11 | 12 | class FaceDataset(Dataset): 13 | def __init__(self, path, transforms=None): 14 | self.path = path 15 | self.keys = ('images', 'labels') 16 | assert os.path.exists(path), 'file `{}` not exists!'.format(path) 17 | 18 | with zarr.LMDBStore(path) as store: 19 | zarr_db = zarr.group(store=store) 20 | self.num_examples = zarr_db['labels'].shape[0] 21 | self.datasets = None 22 | 23 | if transforms is None: 24 | transforms = { 25 | 'labels': lambda v: torch.tensor(v, dtype=torch.long), 26 | 'images': lambda v: torch.tensor((v - 127.5)/127.5, dtype=torch.float32) 27 | } 28 | self.transforms = transforms 29 | 30 | def __len__(self): 31 | return self.num_examples 32 | 33 | def __getitem__(self, idx): 34 | if self.datasets is None: 35 | store = zarr.LMDBStore(self.path) 36 | zarr_db = zarr.group(store=store) 37 | self.datasets = {key: zarr_db[key] for key in self.keys} 38 | 39 | items = [] 40 | for key in self.keys: 41 | item = self.datasets[key][idx] 42 | if key in self.transforms: 43 | item = self.transforms[key](item) 44 | items.append(item) 45 | return items 46 | 47 | 48 | class Model(nn.Module): 49 | def __init__(self, input_size=96 * 96 * 3, output_size=126, 50 | hidden_size=25): 51 | super().__init__() 52 | 53 | self.layer1 = nn.Sequential( 54 | nn.Conv2d(3, 16, kernel_size=6, stride=2, padding=2), 55 | nn.BatchNorm2d(16), nn.ReLU(), nn.MaxPool2d( 56 | kernel_size=2, stride=2)) 57 | self.layer2 = nn.Sequential( 58 | nn.Conv2d(16, 32, kernel_size=6, stride=2, padding=2), 59 | nn.BatchNorm2d(32), nn.ReLU(), nn.MaxPool2d( 60 | kernel_size=2, stride=2)) 61 | self.fc = nn.Linear(6 * 6 * 32, output_size) 62 | self.criteria = nn.CrossEntropyLoss() 63 | 64 | def forward(self, inputs): 65 | outputs = self.layer1(inputs) 66 | outputs = self.layer2(outputs) 67 | outputs = outputs.reshape(outputs.size(0), -1) 68 | outputs = self.fc(outputs) 69 | return outputs 70 | 71 | 72 | def main(batch_size=64, epochs=50): 73 | data_train = FaceDataset('data/anime_faces/train.lmdb') 74 | device = torch.device('cuda' if torch.cuda.is_available() else 'cpu') 75 | 76 | loader = DataLoader(data_train, batch_size=batch_size, num_workers=10) 77 | model = Model() 78 | model.to(device) 79 | model.train() 80 | optim = torch.optim.Adam(model.parameters(), lr=0.001) 81 | for epoch in trange(epochs): 82 | t = tqdm(loader) 83 | for i, (images, labels) in enumerate(t): 84 | images = images.to(device) 85 | labels = labels.to(device) 86 | 87 | optim.zero_grad() 88 | logits = model(images) 89 | loss = model.criteria(logits, labels) 90 | loss.backward() 91 | optim.step() 92 | 93 | predicts = torch.argmax(F.softmax(logits, dim=1), dim=1) 94 | accuracy = (predicts == labels).to(torch.float32).mean() 95 | t.set_postfix( 96 | epoch=epoch, i=i, loss=loss.item(), accuracy=accuracy.item()) 97 | 98 | data_val = FaceDataset('data/anime_faces/val.lmdb') 99 | val_loader = DataLoader(data_val, batch_size=batch_size, num_workers=0) 100 | total = len(data_val) 101 | total_correct = 0 102 | model.eval() 103 | for images, labels in val_loader: 104 | images = images.to(device) 105 | labels = labels.to(device) 106 | logits = model(images) 107 | predicts = torch.argmax(F.softmax(logits, dim=1), dim=1) 108 | correct = (predicts == labels).sum() 109 | total_correct += correct.item() 110 | print('Val accuracy = {}'.format(total_correct / total)) 111 | 112 | 113 | if __name__ == '__main__': 114 | main() 115 | --------------------------------------------------------------------------------