2015-04-27

Introduction to ID3 algorithm - Thuật toán quy nạp cây ID3

Introduction to ID3 algorithm - Thuật toán quy nạp cây ID3 (ID3 algorithm)

Note: một số thứ căn bản trong ML

Introduction

ID3 (Iterative Dichotomiser 3 do J. Ross Quinlan phát triển tại University of Sydney và giới thiệu năm 1986) biểu diễn các khái niệm (concept) ở dạng các cây quyết định (decision tree)
§  Input: một tập hợp các ví dụ, mỗi ví dụ bao gồm các thuộc tính mô tả một tình huống, hay một đối tượng nào đó, và một giá trị phân loại của nó.
§  Output: cây quyết định có khả năng phân loại đúng đắn các ví dụ trong tập dữ liệu rèn luyện, và hy vọng là phân loại đúng cho cả các ví dụ chưa gặp trong tương lai.

Tập huấn luyện gọi là training set bao gồm nhiều records. Mỗi record là một  tập hợp các thuộc tính (attributes). Mục tiêu là cho dữ liệu của các đối tượng cùng lớp (cùng thuộc tính lớp) của nó, xác định lớp của các đối tượng chưa biết (previously unseen) từ các thuộc tính của những đối tượng này.
Biểu diễn cây quyết định (decision tree representation)
§  Các node không phải node lá (non-leaf node) gọi là node quyết định (decision node).
§  Mỗi node là một kiểm định/một kiểm tra (a test on an attribute) trên một thuộc tính (là thuộc tính ordinal hay nomimal), mỗi giá trị của thuộc tính tương ứng với một nhánh của cây. Tức đối tượng nào đi qua node quyết định sẽ di chuyển về nhánh tương ứng.
§  Mỗi node có thể có nhiều node con (mỗi node con cho một test value).
§  Các node lá (leaf node) được gán nhãn (labeled) bởi một nhãn lớp (class label) hay là một phân lớp/loại (a classification).
§  Cây quyết định thể hiện các giả thuyết phân biệt (disjunctive hypotheses).
§  Mỗi đường dẫn tới node lá (leaf node) thể hiện một luật kết hợp (conjuctive term).

ID3 dựa trên thuật toán CLS (Concept Learning System). CLS dựa trên training set C:
§  Bước 1: nếu tất cả các instances trong C là tích cực (positive) à tạo node YES và dừng lại. Nếu tất cả tiêu cực (negative) tạo node NO và dừng lại. Nếu không chọn một tính năng F (feature), với mỗi giá trị v1, v2, …, vn và tạo ra một node quyết định
§  Bước 2: phân hoạch (partition) training set C thành các subset C1, C2, …, Cn theo các giá trị của V
§  Bước 3: áp dụng thuật toán đệ quy cho Ci

Lưu ý: người/trình huấn luyện (trainer) hay chuyên gia (expert) quyết định tính năng nào được chọn.
ID3 cải tiến CLS bằng cách thêm lựa chọn tính năng/thuộc tính (feature selection) bằng heuristic (sẽ trình bày sau). ID3 sẽ tìm trong các thuộc tính của training set thuộc tính nào có khả năng phân tách tốt nhất các ví dụ trong training set.

Nếu có thuộc tính có thể phân loại hoàn toàn training set thì ID3 sẽ dừng lại. Nếu không có thuộc tính như vậy nó sẽ hoạt động đệ quy trên n subset đã phân hoạch (partitioned subsets) để tìm thuộc tính “tốt nhất” tiếp theo. Thuật toán này sử dụng greedy search (tìm kiếm tham lam) tức luôn chọn cái tốt nhất và không bao giờ xem xét lại lựa chọn đã thực hiện.

Vì không có khả năng quay lui trong khi tìm kiếm nên nó có thể gặp phải những hạn chế giống như giải thuật leo núi, đó là hội tụ về cực tiểu địa phương. Trong quá trình tìm kiếm, giải thuật ID3 có xu hướng chọn cây quyết định ngắn hơn là những cây quyết định dài. Đây là tính chất thiên lệch quy nạp của ID3. Cây quyết định sẽ không thay đổi cho đến khi thực hiện lại việc xây dựng cây quyết định với training set mới.

Dữ liệu train của bài toán kinh điển “Có đi chơi tennis/golf hay không?” như sau (dữ liệu sample của Weka weather.arff).
§  Quang cảnh outlook {sunny, overcast, rainy}
§  Nhiệt độ temperature real (số thực)
§  Độ ẩm humidity real (số thực)
§  Gió windy {TRUE, FALSE} (binary)
§  Chơi tennis play {yes, no} (class labeled)

Outlook
Temperature
Humidity
Windy
Play
sunny
85
85
FALSE
no
sunny
80
90
TRUE
no
overcast
83
86
FALSE
yes
rainy
70
96
FALSE
yes
rainy
68
80
FALSE
yes
rainy
65
70
TRUE
no
overcast
64
65
TRUE
yes
sunny
72
95
FALSE
no
sunny
69
70
FALSE
yes
rainy
75
80
FALSE
yes
sunny
75
70
TRUE
yes
overcast
72
90
TRUE
yes
overcast
81
75
FALSE
yes
rainy
71
91
TRUE
no

Sau khi transform data thành nominal (hay dùng weather.nominal.arff):
§  Nhiệt độ temperature {hot, mild, cool}
§  Độ ẩm humidity {high, normal}

Dữ liệu như sau

Day
Outlook
Temperature
Humidity
Windy
Play
D1
sunny
hot
high
FALSE
no
D2
sunny
hot
high
TRUE
no
D3
overcast
hot
high
FALSE
yes
D4
rainy
mild
high
FALSE
yes
D5
rainy
cool
normal
FALSE
yes
D6
rainy
cool
normal
TRUE
no
D7
overcast
cool
normal
TRUE
yes
D8
sunny
mild
high
FALSE
no
D9
sunny
cool
normal
FALSE
yes
D10
rainy
mild
normal
FALSE
yes
D11
sunny
mild
normal
TRUE
yes
D12
overcast
mild
high
TRUE
yes
D13
overcast
hot
normal
FALSE
yes
D14
rainy
mild
high
TRUE
no

Trong quá trình xây dựng cây quyết định việc xác định đâu là node root (node gốc) thường thực hiện bằng cách dùng entropy.

Performance evaluation

Đánh giá cây ID3 thực hiện theo cách thông thường như hold-out (test split) sử dụng validation set với tỷ lệ khoảng 2/3.

Attribute Selection

Làm thế nào để ID3 quyết định thuộc tính nào là tốt nhất? Trong trường hợp này một thuộc tính thống kê (statistical property) được sử dụng, gọi là độ tăng thông tin hay độ lợi thông tin (information gain). Độ lợi này đo lường thuộc tính được chọn phân tách training set thành các lớp mục tiêu (targeted classes) tốt như thế nào? Attribute với IG cao nhất (thông tin hữu ích nhất cho phân loại) sẽ được chọn. Đầu tiên là tìm hiểu khái niệm entropy từ lý thuyết thông tin, entropy đo lường lượng thông tin trong một thuộc tính hay để đo tính thuần nhất (hay ngược lại là độ pha trộn) của một tập hợp. Một tập hợp là thuần nhất nếu như tất cả các phần tử của tập hợp đều thuộc cùng một loại, và khi đó tập hợp này có độ pha trộn là thấp nhất.

Khi một tập hợp là thuần nhất thì có thể biết chắc chắn về giá trị phân loại của một đối tượng (instance) thuộc tập hợp này, hay lượng thông tin có được về tập hợp đó là cao nhất. Khi một tập hợp có độ pha trộn cao nhất, nghĩa là số lượng các đối tượng có cùng giá trị phân loại cho mỗi loại là tương đương nhau, thì khi đó khó có thể đoán chính xác một ví dụ có thể có giá trị phân loại gì, hay nói khác hơn, lượng thông tin ta có được về tập này là ít nhất.

Khái niệm entropy của một tập hợp S được định nghĩa trong Lý thuyết thông tin được giới thiệu bởi Claude E. Shannon là số lượng mong đợi các bit cần thiết để mã hóa thông tin về lớp của một thành viên rút ra một cách ngẫu nhiên từ tập hợp S (xem thêm entropy thông tin). Trong trường hợp tối ưu, giá trị mã hóa có độ dài ngắn nhất (ít bit nhất). Giá trị mã hóa có độ dài tối ưu là –log2p bits cho thông điệp có xác suất là p.

Một số ví dụ:
§  Entropy(S) = 0 khi tất cả các đối tượng của S chỉ cùng 1 loại hay S thuần nhất (perfectly classified)
§  Entropy(S) = 1 khi S có độ pha trộn cao nhất (totally random)
§  0 < Entropy(S) < 1 khi S có số lượng các đối tượng thuộc các phân loại không bằng nhau

Tổng quát hơn một tập hợp S bao gồm c phân loại thì công thức tính entropy của S
${\rm{Entropy}}\left( {\rm{S}} \right) = \mathop \sum \limits_{i = 1}^c  - p\left( I \right){\log _2}p\left( I \right)$

Trong đó p(I) là tỷ lệ S thuộc lớp I.
Ví dụ cụ thể S là tập hợp có 14 đối tượng với 9 YES và 5 NO
Khi đó:
${\rm{Entropy}}\left( {\rm{S}} \right) =  - \left( {9/14} \right){\log _2}\left( {9/14} \right) - \left( {5/14} \right){\log _2}\left( {5/14} \right) = 0.940$

Tiếp theo là việc tính toán Gain(S, A) gọi là độ lợi thông tin thu được, đơn giản là lượng giảm entropy mong đợi gây ra bởi việc phân chia các ví dụ theo thuộc tính này.
${\rm{Gain}}\left( {{\rm{S}},{\rm{\;A}}} \right){\rm{\;}} = {\rm{\;Entropy}}\left( {\rm{S}} \right){\rm{\;}} - {\rm{\;}}\mathop \sum \limits_{v\; \in \;values\left( A \right)} \frac{{\left| {{S_v}} \right|}}{{\left| {\rm{S}} \right|}}{\rm{Entropy}}\left( {{S_v}} \right)$

Trong đó
§  values(A) tập hợp các giá trị có thể có của A
§  $S_v$ tập con subset của S mang giá trị v
§  $\left| {{S_v}} \right|$ số lượng phần tử của $S_v$
§  $\left| {{S}} \right|$ số lượng phần tử của S

Ví dụ tập S là tập hợp có 14 đối tượng, A là thuộc tính gió strong hay weak tương ứng TRUE/FALSE. Phân loại của S là 9 YES và 5 NO. Với attribute wind = weak có 8 trường hợp và wind = strong có 6 trường hợp. Với wind = weak 8 trường hợp bao gồm 6 YES và 2 NO. Với wind = strong 6 trường hợp bao gồm 3 YES và 3 NO (totally random entropy = 1)
${\rm{Gain}}\left( {{\rm{S}},{\rm{\;Wind}}} \right){\rm{\;}} = {\rm{\;Entropy}}\left( {\rm{S}} \right){\rm{\;}} - \left( {8/14} \right)Entropy\left( {{S_{weak}}} \right) - \left( {6/14} \right)Entropy\left( {{S_{strong}}} \right)$
$= 0.940{\rm{\;}} - {\rm{\;}}\left( {8/14} \right)0.811{\rm{\;}} - {\rm{\;}}\left( {6/14} \right)1.00 = 0.048$
${\rm{Entropy}}\left( {{S_{weak}}} \right) =  - \left( {6/8} \right){\log _2}\left( {6/8} \right) - \left( {2/8} \right){\log _2}\left( {2/8} \right) = 0.811$
${\rm{Entropy}}\left( {{S_{strong}}} \right) =  - \left( {3/6} \right){\log _2}\left( {3/6} \right) - \left( {3/6} \right){\log _2}\left( {3/6} \right) = 1$

Ví dụ ID3

Theo data ví dụ các độ đo như sau:
§  Gain(S, Outlook) = 0.246
§  Gain(S, Temperature) = 0.029
§  Gain(S, Humidity) = 0.151
§  Gain(S, Wind) = 0.048 (đã tính toán ở ví dụ trên)

Outlook có gain cao nhất nên sẽ được chọn làm root node. Bây giờ ví dụ theo nhánh sunny sẽ có 5 trường hợp Ssunny = {D1, D2, D8, D9, D11}
Tiếp tục tính toán Gain theo nhánh này:
§  Gain(Ssunny, Humidity) = 0.970
§  Gain(Ssunny, Temperature) = 0.570
§  Gain(Ssunny, Wind) = 0.019

Theo đó humidity có gain cao nhất, sau bước này thì humidity có thể dùng để phân lớp hoàn toàn (classified perfectly) nên không cần xét thêm thuộc tính nào.

Day
Outlook
Temperature
Humidity
Windy
Play
D1
sunny
hot
high
FALSE
no
D2
sunny
hot
high
TRUE
no
D8
sunny
mild
high
FALSE
no
D9
sunny
cool
normal
FALSE
yes
D11
sunny
mild
normal
TRUE
yes

ID3 tree cuối cùng như sau
Các ID3 tree có thể biểu diễn các luật (rule) dưới dạng phát biểu như sau:
§  IF outlook = sunny AND humidity = high THEN play = no
§  IF outlook = rain AND humidity = high THEN play = no
§  IF outlook = rain AND wind = strong THEN play = yes
§  IF outlook = overcast THEN play = yes
§  IF outlook = rain AND wind = weak THEN play = yes

ID3 thông thường được cài đặt sẵn trong các ứng dụng như Weka, R. ID3 được dùng trong các trường hợp như chẩn đoán y tế, đánh giá rủi ro tín dụng … và được đánh giá là có hiệu quả.

Kết quả chạy với Weka

=== Run information ===

Scheme:weka.classifiers.trees.Id3
Relation:     weather.symbolic
Instances:    14
Attributes:   5
              outlook
              temperature
              humidity
              windy
              play
Test mode:split 66.0% train, remainder test

=== Classifier model (full training set) ===

Id3


outlook = sunny
|  humidity = high: no
|  humidity = normal: yes
outlook = overcast: yes
outlook = rainy
|  windy = TRUE: no
|  windy = FALSE: yes

Time taken to build model: 0 seconds

=== Evaluation on test split ===
=== Summary ===

Correctly Classified Instances           3               60      %
Incorrectly Classified Instances         2               40      %
Kappa statistic                          0    
Mean absolute error                      0.4  
Root mean squared error                  0.6325
Relative absolute error                 84.6154 %
Root relative squared error            128.7453 %
Total Number of Instances                5    

=== Detailed Accuracy By Class ===

               TP Rate   FP Rate   Precision   Recall  F-Measure   ROC Area  Class
                 1         1          0.6       1         0.75       0.5      yes
                 0         0          0         0         0          0.5      no
Weighted Avg.    0.6       0.6        0.36      0.6       0.45       0.5 

=== Confusion Matrix ===

 a b   <-- as="" classified="" o:p="">
 3 0 | a = yes
 2 0 | b = no

Các vấn đề

Vấn đề overfitting? Xem xét sai số của giả thuyết h (error of hypothesis h) như sau:
§  Trong training data: errortrain(h)
§  Trong toàn bộ dữ liệu D (entire distribution D of data): errorD(h)

Giả thuyết h  H gọi là overfit training data nếu tồn tại một giả thuyết khác h’  H thỏa mãn
§  errortrain(h) < errortrain(h’)
§  errorD(h) > errorD(h’)

Có nghĩa overfitting là do bị ảnh hưởng quá nhiều vào dữ liệu rèn luyện mà không cho kết quả tốt trên dữ liệu thật sự. Ví dụ overfitting có thể thực hiện bằng cách thêm 1 record #D15 như sau sunny-hot-normal-strong-play=No
Nhiều giải pháp được đưa ra như cắt tỉa lại cây quyết định (prunning tree) sau khi học, hoặc cắt tỉa các luật sau khi chuyển cây về dạng luật. Một vấn đề nữa là thuộc tính có giá trị liên tục. Để giải quyết các vấn đề này một số thế hệ sau của ID3 đã ra đời. Nổi bật trong số đó có thể kể như C4.5 (Quinlan, 1996) (J48 trong Weka).

2015-03-16

LZMA compressed SWF file vs 7z LZMA file

Lần này có việc phải decompress SWF file dùng LZMA, nên note lại cho nhớ.
Nếu sử dụng Python thì nên cài pylzma xong xài script tại đây
https://github.com/OpenGG/swfzip
Cần thiết thì chỉnh 1 chút xíu là dùng được.

Nếu muốn viết tool thay vì chạy script thì có thể dùng library của 7z. Về header của 2 loại file sẽ khác nhau chút đỉnh, nên khi dùng library 7z sẽ không decompress được file ZWS.

Chi tiết nó có ghi ở đây nhưng khá là khó xem (Adobe Flash)

Issue


When you decompress an LZMA-compressed SWF file using the ByteArray.uncompress method, an exception is thrown.

Solution


The LZMA header format Flash authoring uses does not match the header 7z format uses. You can convert the SWF file in the LZMA format into the 7z LZMA format by updating the header.
https://helpx.adobe.com/flash-player/kb/exception-thrown-you-decompress-lzma-compressed.html

Mình sẽ note lại cho kỹ header 2 thằng này như sau, cần phải điều chỉnh lại thông tin trước khi thực hiện decompress với 7z.

//-----------------------------------------------------------------
// SWF file LZMA header
//-----------------------------------------------------------------
// bytes 0-3: ZWS+version 
// bytes 4-7: Uncompressed length, the uncompressed length of the SWF data 
// (includes ZWS+version (4 bytes) and uncompressed length (4 bytes)) 
// bytes 8-11: Compressed length 
//      Compressed length does not include header (4+4+4 bytes) or lzma props (5 bytes)
//      Compressed length does include LZMA end marker (6 bytes)
// bytes 12-16: LZMAproperties 
// bytes 17-n: Compressed data
//
// 17 bytes = 12 bytes header + 5 bytes LZMA properties
//-----------------------------------------------------------------

//-----------------------------------------------------------------
// 7z LZMA header
//-----------------------------------------------------------------
// bytes 0-4: LZMA properties 
// bytes 5-12: Uncompressed length (take the 
// swf lzma length - 8 (don't include ZWS+version + uncompressed length))
// bytes 13-n: Compressed data            
//
// 13 bytes  = 8 bytes uncompressed length + 5 bytes LZMA properties
//-----------------------------------------------------------------

SevenZip.Compression.LZMA.Decoder coder = new SevenZip.Compression.LZMA.Decoder();

// Read the decoder properties (bytes 12-16)
byte[] properties = swfBytes.Skip(12).Take(5).ToArray();
// Calculate uncompressed length (bytes 4-7), subtract 8 (remove header bytes) long len = Le2Int(swfBytes, 4) - 8; coder.SetDecoderProperties(properties); byte[] decompressedData; using (Stream input = new MemoryStream(swfBytes.Skip(17).ToArray())) using (MemoryStream output = new MemoryStream()) { coder.Code(input, output, input.Length, len, null); decompressedData = output.ToArray(); }




Đoạn code chuyển từ int sang bytes, little endian

int Le2Int(byte[] data, int offset = 0)
{
    return (data[offset + 3] << 24)
            | (data[offset + 2] << 16)
            | (data[offset + 1] << 8)
            | data[offset];
}

byte[] Int2Le(int data)
{
    byte[] bytes = new byte[4];
    bytes[0] = (byte)data;
    bytes[1] = (byte)(((uint)data >> 8) & 0xFF);
    bytes[2] = (byte)(((uint)data >> 16) & 0xFF);
    bytes[3] = (byte)(((uint)data >> 24) & 0xFF);
    return bytes;
}
Với Python, unzip các file trong folder. Nếu unzip file xem trong swfzip


#!/usr/bin/python
#
# This script is inspired by jspiro's swf2lzma repo: https://github.com/jspiro/swf2lzma
#
#
# SWF Formats:
## ZWS(LZMA)
## | 4 bytes       | 4 bytes    | 4 bytes       | 5 bytes    | n bytes    | 6 bytes         |
## | 'ZWS'+version | scriptLen  | compressedLen | LZMA props | LZMA data  | LZMA end marker |
##
## scriptLen is the uncompressed length of the SWF data. Includes 4 bytes SWF header and
## 4 bytes for scriptLen itself
##
## compressedLen does not include header (4+4+4 bytes) or lzma props (5 bytes)
## compressedLen does include LZMA end marker (6 bytes)
#
import os
import pylzma
import sys
import struct
import zlib
import shutil

def check(test, msg):
    test or exit("Error: \n" + msg)
    
def debug(msg, level = "info"):
    print '%s : %s' %(level, msg)

def confirm(prompt, resp = False):
    """prompts for yes or no response from the user. Returns True for yes and
    False for no.

    'resp' should be set to the default value assumed by the caller when
    user simply types ENTER.

    >>> confirm(prompt='Create Directory?', resp=True)
    Create Directory? [y]|n:
    True
    >>> confirm(prompt='Create Directory?', resp=False)
    Create Directory? [n]|y:
    False
    >>> confirm(prompt='Create Directory?', resp=False)
    Create Directory? [n]|y: y
    True

    """

    if prompt is None:
        raise Exception('Not valid prompt')

    if resp:
        prompt = '%s %s/%s: ' % (prompt, 'Y', 'n')
    else:
        prompt = '%s %s/%s: ' % (prompt, 'N', 'y')

    while True:
        ans = raw_input(prompt)
        print ''
        if not ans:
            return resp
        if ans not in ['y', 'Y', 'n', 'N']:
            print 'please enter y or n.'
            continue
        if ans == 'y' or ans == 'Y':
            return True
        if ans == 'n' or ans == 'N':
            return False

def unzip(inData):
    if inData[0] == 'C':
        # zlib SWF
        debug('zlib compressed swf detected.')
        decompressData = zlib.decompress(inData[8:])
    elif inData[0] == 'Z':
        # lzma SWF
        debug('lzma compressed swf detected.')
        decompressData = pylzma.decompress(inData[12:])
    elif inData[0] == 'F':
        # uncompressed SWF
        debug('Uncompressed swf detected.')
        decompressData = inData[8:]
    else:
        #exit('not a SWF file')
        decompressData = []
        return decompressData

    sigSize = struct.unpack("<I", inData[4:8])[0]
    debug('Filesize in signature: %s' % sigSize)

    decompressSize = len(decompressData) +8
    debug('Filesize decompressed: %s' % decompressSize)

    check((sigSize == decompressSize), 'Length not correct, decompression failed')
    header = list(struct.unpack("<8B", inData[0:8]))
    header[0] = ord('F')

    debug('Generating uncompressed data')
    return struct.pack("<8B", *header) + decompressData

def zip(inData, compression):
    if(compression == 'lzma'):
        check((inData[0] != 'Z'), "already LZMA compressed")

        rawSwf = unzip(inData);

        debug('Compressing with lzma')
        compressData = pylzma.compress(rawSwf[8:], eos=1)
        # 5 accounts for lzma props

        compressSize = len(compressData) - 5

        header = list(struct.unpack("<12B", inData[0:12]))
        header[0]  = ord('Z')
        header[3]  = header[3]>=13 and header[3] or 13
        header[8]  = (compressSize)       & 0xFF
        header[9]  = (compressSize >> 8)  & 0xFF
        header[10] = (compressSize >> 16) & 0xFF
        header[11] = (compressSize >> 24) & 0xFF

        debug('Packing lzma header')
        headerBytes = struct.pack("<12B", *header);
    else:
        check((inData[0] != 'C'), "already zlib compressed")

        rawSwf = unzip(inData);

        debug('Compressing with zlib')
        compressData = zlib.compress(rawSwf[8:])

        compressSize = len(compressData)

        header = list(struct.unpack("<8B", inData[0:8]))
        header[0] = ord('C')
        header[3]  = header[3]>=6 and header[3] or 6

        debug('Packing zlib header')
        headerBytes = struct.pack("<8B", *header)

    debug('Generating compressed data')
    return headerBytes+compressData

def process(infile, outfile, operation='unzip', compression='zlib'):
    fi = open(infile, "rb")
    infileSize = os.path.getsize(infile)
    inData = fi.read()
    fi.close()
    
    debug('Reading ' + os.path.basename(infile) + ' ver ' + inData[0:3])

    #check((inData[1] == 'W') and (inData[2] == 'S'), "not a SWF file")
    if not ((inData[1] == 'W') and (inData[2] == 'S')):
        return

    if(operation=='unzip'):
        outData = unzip(inData)
        
        if len(outData) == 0:
            return
        
        increment = round(100.0 * len(outData) / infileSize) - 100
        print 'File decompressed, size increased: %d%%' % increment
    else:
        compression = compression == 'lzma' and 'lzma' or 'zlib'
        outData = zip(inData, compression)
        decrement = increment = 100 - round(100.0 * len(outData) / infileSize)
        print 'File compressed with %s, size decreased: %d%% %s' % (compression, decrement,
            decrement<0 and '\n\nNotice: Recompressing may cause filesize increased' or'')

    fo = open(outfile, 'wb')
    fo.write(outData)
    fo.close()

if __name__ == "__main__":
    currentDir = os.getcwd()
    if len(sys.argv) > 1:
        currentDir = sys.argv[1]

    files = []
    if os.path.isdir(currentDir):
        files = os.listdir(currentDir)
    else:
        # Arg is file path?
        files = [ currentDir ]
        currentDir = os.path.dirname(os.path.abspath(currentDir))

    outputDir = os.path.join(currentDir, "unzip")
    if not os.path.isdir(outputDir):
        os.mkdir(outputDir)

    scriptFile = os.path.basename(sys.argv[0])

    for inputFile in files:
        if os.path.isdir(inputFile):
            continue

        [name, ext] = os.path.splitext(os.path.basename(inputFile))
        outputFile = os.path.join(outputDir, name + "_unzip" + ext)
        inputFile = os.path.join(currentDir, os.path.basename(inputFile))
        process(inputFile, outputFile)

    sys.exit(0)

2015-02-14

CFA, SEM từ từ rồi cũng ngấm ...

Ngày gần Tết, rảnh rỗi note lại cho nhớ...

Mình làm luận văn cơ sở lý thuyết này nọ từ cách đây 2 năm. Xong rồi lấy data, rồi tập tành phân tích với AMOS. Thiệt ra thì đến giờ vẫn dậm chân tại đó, mình không muốn đổi sang 1 cái khác vì cảm thấy có cái gì đó ko trọn vẹn. Cái bằng thì ko có ảnh hưởng gì quan trọng với công việc của mình, cũng ko có gì quá đặc biệt. nhưng có nhiều ý nghĩa khác, cảm giác "đầu hàng" ko dễ chịu lắm. Nói chung mình cũng rút ra nhiều điều. Lúc đầu cũng coi tá lả mọi thứ nhưng hình như ko thể lĩnh hội nổi vì nó quá lung tung mà ko có hệ thống gì cả. Sách download về thì 1 đống nhưng đọc 1 đoạn là thấy ko ăn nhập gì (tâm lý muốn đọc liền xài liền). Sau đó thì bận quá nên mình cũng cứ để vậy, lâu lâu buồn thì lại search và đọc tiếp cuốn của Kline (thiệt là khó nhai). Thế rồi phải coi lại hết 1 loạt các thứ, từ thống kê tới những thứ đã từng học từ lâu lắc mà mình đã từng nghĩ cũng ko cần hiểu nhiều về mặt toán học của nó như PCA, PAF, EFA... Thế mới nói đời rất "ngu", lúc đi học thì không học cho đàng hoàng, lúc cần thì mới lôi ra. 

Mà nhiều cái gọi là tutorial trên Internet cũng rất là "tào ...". Mình lấy ví dụ 1 cái cơ bản như CFA vs SEM là gì chẳng hạn thì trong những cái mình download lúc đầu cũng đã sai tè le, nói chung là cứ blah blah mà sai be bét ra, cơ bản là những người viết ra cũng ko hiểu (nguy hiểm thật ... làm mình cũng hoảng). Viết thì viết là phân tích CFA với AMOS mà lại chưng cái model của SEM, rồi cũng phân tích đánh giá tá lả (may sao 2 cái nó cùng một cách phân tích)? Mà ngay trong những luận văn mình có được cũng ghi quy trình nghiên cứu chung chung dạng tránh né và cũng chạy SEM xong thôi.

Nếu mà để ý kỹ thì trên wiki có 1 đoạn sẽ dễ hiểu 
CFA is also frequently used as a first step to assess the proposed measurement model in a structural equation model. Many of the rules of interpretation regarding assessment of model fit and model modification in structural equation modeling apply equally to CFA. CFA is distinguished from structural equation modeling by the fact that in CFA, there are no directed arrows between latent factors. In other words, while in CFA factors are not presumed to directly cause one another, SEM often does specify particular factors and variables to be causal in nature. In the context of SEM, the CFA is often called 'the measurement model', while the relations between the latent variables (with directed arrows) are called 'the structural model'.

Tức là không có có direct arrow giữ các latent factor. Mà latent factor hay latent variable thì không được đo trực tiếp, ký hiệu bằng cái hình ellipse.

Học hành mà có người chỉ dẫn thì rất là nhanh, còn mò thì rất là lâu nhưng từ từ thì sẽ ngấm :D

2014-09-15

Error when installing windows SDK 7.1

Thực hiện install các bước như sau:

Download Microsoft Windows SDK for Windows 7 and .NET Framework 4 (ISO)
http://www.microsoft.com/en-us/download/details.aspx?id=8442
  • GRMSDK_EN_DVD.iso is a version for x86 environment. 
  • GRMSDKX_EN_DVD.iso is a version for x64 environment. 
  • GRMSDKIAI_EN_DVD.iso is a version for Itanium environment.
> download GRMSDKX_EN_DVD.iso cho x64

Một số lỗi có thể tham khảo tại đây:
http://blogs.msdn.com/b/windowssdk/archive/2009/09/16/windows-7-sdk-setup-common-installation-issues-and-fixes.aspx

Một số lỗi khác:
  • Có installed Microsoft Visual Studio C++ 2010 SP1 (Visual Studio 2010 SP1).
  • Có .NET Framework 4.5/4.6 installed (Visual Studio 2012/2013)
Thực hiện:
  • Uninstall Visual Studio C++ 2010 Redist packages
  • Uninstall .NET Framework 4.5/4.6.
  • Install SDK 7.1
  • Reinstall Visual Studio C++ 2010 Redist packages
  • Reinstall .NET Framework 4.5/4.6.
Microsoft Visual Studio C++ 2010 SP1 already installed (both SDK 7.1 and VC++ 2010 SP1 installed)
> Apply the SDK 7.1 patch
Download Microsoft Visual C++ 2010 Service Pack 1 Compiler Update for the Windows SDK 7.1

SDK 7.1 was installed (without Microsoft Visual C++ 2010 SP1)
> Upgrade Microsoft Visual Studio 2010 to SP1

Microsoft Visual Studio 2010 Service Pack 1 (Installer)
Xem Additional Information for ISO format

Microsoft Visual C++ 2010 SP1 Redistributable Package (x64)

2014-02-04

Create a .deb Debian package (tạo một package.deb)

Để tạo một .deb package có thể tham khảo các tài liệu

How to create Debian packages

Mình chỉ tóm tắt thôi, nó gồm các bước như sau:

  1. Tạo thư mục dạng ${PACKAGE_NAME}_${VERSION}_${ARCHITECTURE}, ví dụ your-app_0.4.5-r5_i386.

    Nếu build .deb cho Ubuntu có thể chỉ định thêm Ubuntu version ví dụ your-app_0.4.5-r5ubuntu1_i386 có nghĩa là version 1 dành cho Ubuntu build từ version 0.4.5-r5 dành cho Debian.

  2. Tạo các subdirectories cho các file sẽ install, ví dụ:
    /usr/bin ‣ your-app
    /usr/lib/nautilus/extensions-03 ‣ chứa extension cho Nautilus như libnautilus-your-app.so
    /usr/share/doc, /usr/share/man ‣ chứa file help, manual
    /usr/share/locale ‣ file ngôn ngữ, /usr/share/menu.
    /opt/your-app/logo_32.xpm

  3. Tạo thư mục DEBIAN trong package directory. Trong thư mục DEBIAN chứa các file như control, conffile, md5sums, postinst, postrm, preinst, prerm. Có thể tham khảo từ các .deb package khác bằng cách extract nội dung các .deb có sẵn. Thông thường các .deb được cache trong /var/cache/apt/archives. Để xem .deb có nội dung gì có thể dùng ar –t ví dụ

    ar -t /var/cache/apt/archives/firefox_15.0.1+build1-0ubuntu0.11.10.1_i386.deb.
    

    Để extract dùng ar –x sau đó dùng tar để xem nội dung các file nén.
    Có thể dùng tool dpkg-deb để extract nội dung file .deb, eg.

    dpkg-deb -x .../app.deb ~/workspace/app
    dpkg-deb -e .../app.deb ~/workspace/app/DEBIAN
    

  4. Nội dung file DEBIAN/control có thể giống file control của các app khác, chỉ cần chỉnh sửa 1 số field.
  5. File DEBIAN/postinst là một script sẽ run bởi dpkg –i sau khi các file trong package được extract. Post-install bắt đầu bằng
    #! /bin/sh Để bảo đảm nếu có error phát sinh, script nên return non-zero exit code bằng set –e để shell exit ngay command đầu tiên không thành công.

  6. File DEBIAN/preinst pre-install
  7. File DEBIAN/prerm pre-remove
  8. File DEBIAN/postrm post-remove
  9. Xóa tất cả các file backup *~, nhất là các file trong DEBIAN/*~

    rm -rf `find package-dir -name '*~'`
    

  10. Tạo file md5sums chứa checksum MD5 cho tất cả các file trong package bằng command

    md5sum `find . -type f | grep -v '^[.]/DEBIAN/'` > DEBIAN/md5sums
    

    Chạy command trong thư mục chứa DEBIAN.

  11. Giá trị của field Installed-Size: là số cuối cùng trong output của command du chạy trong package directory (lưu ý remove backup file). Có thể chỉ summarize kết quả với command như sau:

    du -sx --exclude DEBIAN package-dirname
    -s, --summarize   ‣display only a total for each argument
    -x, --one-file-system ‣skip directories on different file systems
    

  12. Build .deb package bằng

    fakeroot dpkg-deb –b|--build package-dir
    

  13. Kiểm tra bằng

    lintian package-name.deb
    

  14. Để install .deb package chỉ cần double click, dùng command như sau

    sudo dpkg -i package-file.deb
    

    Để uninstall dùng command

    sudo dpkg -r package-name
    


2013-11-14

Download an Google Chrome Extension file (.crx file)

Download file Google Chrome Extension

Đôi khi cần lưu file .crx version cũ khi không muốn cài lại máy và chỉ dùng version mới nhất thì nên download file .crx. Tuy nhiên nếu mở bằng Google Chrome thì cũng khó download được. Cách tốt nhất là dùng 1 chương trình khác download, dùng browser khác hoặc IDM.

Đầu tiên là vào Chrome Extension tìm ID của extension cần lưu.
Sau đó điền vào đường dẫn 

https://clients2.google.com/service/update2/crx?response=redirect&x=id%3Dxxxxxx%26uc

Phần màu xanh là phần sẽ điền ID vào. Ví dụ ID của Adblock Plus là cfhdojbkjhnklbpkdaibdccddilifddb

Vậy URL để download là 
https://clients2.google.com/service/update2/crx?response=redirect&x=id%3Dcfhdojbkjhnklbpkdaibdccddilifddb%26uc

2013-11-09

Unlock and jailbreak iPhone

Cóp nhặt 1 số thứ liên quan khi dev team nhận được 1 cái iPhone cần phải xử

Unlock là gì, jailbreak là gì?

Là 2 quá trình khác nhau. Tuy nhiên unlock thì cần jailbreak.
Jailbreak là quá trình can thiệp để có thể toàn quyền điều khiển iPhone (lấy quyền root).
Unlock là gì? Can thiệp vào baseband để sử dụng SIM của bất kỳ nhà mạng nào à xem baseband ở phần sau à nếu không unlock thì ko call được. Unlock yêu cầu phải jailbreak trước, sau khi jailbreak xong iPhone sẽ có thể cài appà cài unlock tools như ultrasn0w để unlock. Tất nhiên quá trình unlock còn phụ thuộc nhiều yếu tố trong đó chủ yếu là version của baseband.

Baseband là gì?

Firmware là gì à1 dạng giữa phần mềm và phần cứng.
Baseband thực hiện điều khiển các loại sóng bao gồm sóng điện thoại. Nên để call được với iPhone locked phải can thiệp BB. Khi nâng cấp FW thông thường nâng cấp cả BB. Có thể hạ FW nhưng khi hạ FW thì không hạ được BB. Tại sao phải giữ BB à để thực hiện unlock vì tool unlock chỉ support 1 số BB. Ví dụ: ultrasn0w chỉ support unlock 3GS với các BB sau: 0.6.15, 04.26.08, 05.11.07, 05.13.01, 05.12.01.

Kiểm tra BB của iDevice Settings à General à About à Modem Firmware
Nếu máy chưa activate (dính ở Emergency Call) thì dùng f0recast
Các tools cần thiết có thể download tại đây http://ih8sn0w.com/

Boot loader là gì?

Là đoạn code thực hiện quá trình kiểm tra và nạp firmware theo cách Apple mong muốn. Khi iBoot (boot loader của iPhone) được kích hoạt thì không thể downgrade hay restore custom FW.

Recovery mode là gì?

Là quá trình restore hoặc upgrade firmware có nạp iBoot. Để thực hiện cần phải đưa về recovery mode. Ở chế độ này trên màn hình sẽ hiện cọng cáp kết nối logo iTunes.
Cách đưa về recovery mode :
1. Tắt nguồn, tháo cáp
2. Giữ phím Home
3. Trong khi giữ phím Home thì cắm cáp vào
4. Tiếp tục giữ đến khi hiện hình cái cáp và mũi tên hướng lên logo Itunes là máy đã đuợc đưa về Recovery mode.
Cách thoát khỏi Recovery mode:
Giữ cùng lúc phím Home + Power trong 10s đến khi Iphone tắt.

DFU mode là gì?

Là Device Firmware Upgrade, như đã nói đây là 1 chế độ đặc biệt cần khi downgrade firmware. Tại mode này iBoot và OS sẽ bị bỏ qua, iTunes vẫn nhận iPhone nhưng mà hình iPhone đen thui.
Để enter mode này có thể dùng các tool hỗ trợ để việc thực hiện dễ dàng hơn.

Thực hiện manual như sau:
1. Chạy iTunes
2. Cắm phone vào PC
3. Chờ iTunes nhận (detected) được iphone
4. Bấm và giữ nút Power (ở trên) khoảng 3s . Sau đó bấm tiếp nút Home (nút tròn) và giữ (trong khi vẫn giữ nút Power) khoảng 10s.
5. Trong khoảng 3s thì iPhone sẽ tắt và khởi động lại. Màn hình sẽ tối đen. Lúc này đếm nhẩm trong đầu từ 1 -10, khi đếm tới 10 thì buông nút Power ra nhưng vẫn bấm và giữ nút Home. Nên để ý là khi buông nút Power ra thì màn hình phải vẫn còn tối đen. Nếu lúc buông Power mà quả táo đã hiện lên là đã chậm tay và phải làm lại từ đầu. Cái mẹo ở lúc này là phải buông Power trước khi quả táo xuất hiện!
6. Lúc này tay vẫn còn giữ nút Home, chờ thêm vài giây nữa thì iTunes sẽ báo là "detect phone in DFU mode". Lúc này có thể buông nút Home.
Cách thoát khỏi DFU mode (chỉ hữu dụng khi mắc kẹt và áp dụng luôn):
Giữ Home và Power trong khoảng 30s cho đến khi nào iTunes báo đã ngắt kết nối với iPhone thì thôi.

Activation là gì?

Kích hoạt iPhone qua screen Emergency Call và vào được màn hình chính là springboard.
Nếu là bản quốc tế thì chỉ cần cắm vào iTunes. 1 dạng khác là cần cắm SIM (SIM lock) của nhà mạng để unlock à gọi là activate không cần jailbreak
Nếu iPhone lock à cần jailbreak hay gọi là hacktivate (hacktivation).

SHSH là gì, ECID là gì?

ECID được viết tắc từ chữ Exclusive Chip ID là mã số của 1 con chip nhỏ mà Apple đã gắn vào Iphone 3GS và Ipod Touch 3G. Apple có thể chứng thực được iPhone nào khi kết nối với Server của Apple. ECID không thể thay đổi được bằng software, chỉ có thể thay đổi bằng cách thay chip khác.
SHSH như là một tờ chứng nhận cho mã số đó dạng file có ext là shsh. SHSH được lưu trữ trên Server của Apple, khi restore iPhone của mình, bước thứ 2 là gian đoạn "Verifying with Apple Server". Đó chính là lúc Itunes đã kết nối với server và đang kiểm chứng xem ECID của mình có hợp lệ hay không thông qua file .shsh đã lưu tại đó. Cydia có hỗ trợ backup SHSH.
Tại sao phải backup SHSH tại Cydia. Apple không cho restore về phiên bản thấp hơn trừ phiên bản gốc. Tức Apple không cung cấp chứng thực cho việc hạ hay restore xuống FW thấp hơn FW hiện tại khi không có SHSH. Thông thường sau 1 thời gian ra FW mới Apple sẽ closed luôn FW cũ tức chỉ có thể nâng cấp lên FW mới nhất.
Khi restore về phiên bản cũ hơn thì trong giai đoạn verifying với server phiên bản đó phải được server hỗ trợ. Có 2 cách giả server Apple iTunes: server nào online đã có sao lưu SHSH (hiện tại có Cydia của Saurik) hoặc tạo local server dùng file đã lưu có tại local. Phải chỉnh file hosts để chuyển server của iTunes là gs.apple.com thành server mong muốn. Khi restore về FW thấp hơn FW hiện hành mà không được Apple hỗ trợ thì phải có bản sao lưu SHSH để tạo custom firmware.

Backup và thao tác với SHSH

Hai tools dùng backup SHSH là iFaith và TinyUmbrella (yêu cầu phải cài JRE)
Có thể dùng iFaith kiểm tra SHSH đã lưu của iPhone, fetch SHSH về từ server nếu có, backup SHSH lên Cydia
Dùng sn0wbreeze để tạo custom FW à dung restore FW mà iPhone ko bị nâng baseband.

Untethered jailbreak và tethered jailbreak

Untethered jailbreak còn gọi là jailbreak “không dính cáp” JBU khác với  tethered jailbreak: nếu chỉ tethered jailbreak thì trong trường hợp máy hết pin hay tắt nguồn nói chung là khi muốn chạy một app JB nào thì phải thực hiện 1 vài thao tác (cắm cable à dùng redsn0w à chạy Just Boot).

BootROM cũ và BootROM mới

Thời điểm Apple phát hành FW iOS 4.0, Dev-Team chỉ tìm được kẽ hở của FW có thể jailbreak cho 3GS Old-BootRom trong khi đó không thể tìm cách jailbreak New-BootRom. Do thấy Old-BootRom bị "hở sườn" nên Apple thay đổi loại iBoot mới cho 3GS là New-BootRom (iBoot-359.3.2).

Ngoài ra, sau khi sản xuất iPhone 4, Apple vẫn tiếp tục sản xuất The New 3GS 8GB với chip Toshiba, mục đich ngăn chận máy Lock nâng baseband của iPad (0.6.15) để unlocked (ultrasn0w hỗ trợ unlock BB 0.6.15). Đó là các The New 3GS 8GB sản xuất từ tuần 33 năm 2011 trở về sau. Serial Number: xx133xxxxxx, firmware 4.3.5, BB 5.14 (một số thông tin khác là từ tuần 27 năm 2011 trở về sau).

Các The New 3GS 8GB này khi jailbreak bằng cách dùng baseband của iPad 0.6.15 sẽ bị lỗi "NO ALL", mất WiFi, Bluetooth, ECID, IMEI. Đồng thời iTunes báo lỗi (-1), và máy bị treo hình cáp dĩa. Giang hồ đồn đây là loại baseband "Emergency Boot Loader" (EBL). Các máy The New 3GS 8GB bị "NO ALL" iTunes Error (-1) không thể restore và chỉ có thê gửi trả bào hành nếu còn thời hạn.

Ví dụ, số serial của iPhone 4 thường có cấu trúc AABCCDDDEEF, trong đó:
AA = Nhà máy sản xuất và ID của máy móc
B: Năm sản xuất (là chữ số đứng cuối mỗi năm, ) 9=2009, 0=2010, 1=2011, 2=2012)
CC: tuần sản xuất
DDD: Unique identifier (tuy nhiên không liên quan tới UDID của sản phẩm)
EE: Màu sản phẩm
F: Kích cỡ bộ nhớ, trong đó S là 16GB và T là 32GB

Với các version cũ hơn thì quy tắc khác một chút. OSX Daily còn cung cấp một bảng hướng dẫn dễ đọc hơn với 3 ký tự cuối cùng của số serial:
VR0 (iPhone 2G 4GB màu bạc)
WH8 (iPhone 2G 8GB màu bạc)
0KH (iPhone 2G 16GB màu bạc)
Y7H (iPhone 3G 8GB màu đen)
Y7K (iPhone 3G 16GB màu đen)
3NP (iPhone 3GS 16GB màu đen)
3NR (iPhone 3GS 32GB màu đen)
3NQ (iPhone 3Gs 16GB màu trắng)
3NS (iPhone 3Gs 32GB màu trắng)
A4S (iPhone 4 16GB màu đen)
A4T (iPhone 4 32GB màu đen)

Do đó với mỗi loại BootROM thì việc jailbreak sẽ thực hiện khác nhau à nếu dung OLD BootROM thì có nhiều lợi thế hơn khi jailbreak.

BootROM cũ có lỗi vì thế có thể thực hiện untethered jailbreak (không dính cáp) và đồng thời cho phép "bypass" signature check (SHSH) khi restore custom FW à lợi hại nếu có OLD BootROM.

Đưa iPhone về DFU mode bằng iReb à có số giây để biết cách thực hiện à cắm iPhone vào để iTunes nhận, iPhone đang bật à thực hiện theo iReb.
Nếu bị dính recovery mode không thoát được theo cách thông thường à dùng TinyUmbrella để thoát recovery mode

Kiểm tra BootROM là OLD hay NEW bằng cách dung iDetector (iPhone phải đưa về DFU mode trước)

Thực hiện

Apple hiện tại đang để 2 FW active cho 3GS là FW 4.1 và FW 6.1.3.
Kiểm tra baseband à nếu là BB 6.15 thì dùng Redsn0w để downgrade BB 05.13.04 à nếu là BB 5.14 , 5.15 , 5.16 (những thằng không được hỗ trợ) thì nâng lên BB 6.15 rồi làm 1 lần nữa để hạ xuống BB 5.13, ngay cả khi device chưa bao giờ có BB 05.13.04 (đừng hỏi vì sao làm được).  
Tại sao hạ xuống BB 05.13 là theo thông tin thì BB này ít tốn pin hơn BB 6.15 và quan trọng iPhone sẽ bị mất chức năng như định vị GPS bởi vì BB 06.15.00 của iPad không chứa bộ nhớ bản đồ và các lệnh giao tiếp Read/Write với chip GPS.
Nếu máy từ tuần 34 của 2011 trở về sau thì theo cảnh báo của redsn0w không nên up lên BB 6.15 à có thể bị dính NO ALL à QUÁ ĐEN à chỉ có nước chờ tool unlock fix thôi.
Kiểm tra nếu OLD BootROM thì OK rồi, nếu NEW BootROW thì phải có SHSH à xem lại backup SHSH à dùng sn0wbreeze để tạo custom FW à restore lại FW 5.1.1 nếu có SHSH của FW 5.1.1. Nếu không có thì bó tay à chỉ có thể dùng active FW là FW 4.1 hoặc FW 6.1.3 và các FW có backup SHSH.

Tóm lại thực hiện như sau:
Dùng redsn0w JB và nâng BB 6.15 à JB và hạ BB xuống 5.13
Nếu chưa lên FW 6.1.3, tạo custom FW 6.1.3 bằng sn0wbreeze  à restore không nâng BB

Download 6.1.3 nếu cần sn0wbreeze  iPhone2,1_6.1.3_10B329_Restore.ipsw

Đảm bảo redsn0w chạy quyền admin, mode compatibility với Windows XP SP2/SP3
Chọn Extract à Select IPSW à chọn FW 6.0 (lưu ý là 6.0 chứ không phải 6.1.3) à Back chọn JB à chỉ check Install baseband Ipad
Làm thêm 1 lần nữa để hạ BB, đồng thời check install Cydia luôn.
Sau bước này thì vẫn chưa có Cydia để install ultrasn0w à chạy Just Boot (Extract à Select IPSW cũng chọn FW 6.0 à đưa máy về DFU chọn Extras à Just Boot) à iPhone hiện hình quả dứa.
Như vậy là Jailbreak Tethered xong à search & install ultrasn0w trong Cydia à unlock.