<jupyter_start><jupyter_text>資料前置<jupyter_code>df = pd.read_csv('CFD_all_Y_v4.csv',encoding='utf_8_sig')
# df為所有損害賠償判決書  DF為分類標籤整理為1:1的判決書資料

df = df.dropna(subset = ['案件描述','判決主文類別'])
df = df[df['判決主文類別'] != 3]
df1 = df[df['判決主文類別'] == 1]
df2 = df[df['判決主文類別'] == 2]
df3 = df1.sample(len(df2), random_state=87)
DF = pd.concat([df3, df2])
DF['判決主文類別'] = DF['判決主文類別']-1
DF = DF.sample(frac=1)                       # 打亂順序

DF['判決主文類別'].value_counts()             # 1代表原告訴求駁回<jupyter_output><empty_output><jupyter_text>文字處理<jupyter_code># 主文斷詞斷句

main_text = DF['案件描述'].tolist()
cat_list = DF['判決主文類別'].tolist()

# 主文斷詞  ['第一篇', '第二篇']
main_text_list = [' '.join(jieba.cut( txt, cut_all=False)) for txt in main_text]

# 主文斷句  [['第一篇第一句','第一篇第二句'], ['第二篇第一句','第二篇第二句']]
main_sentence_list = [re.split('，|。', i) for i in main_text_list]             
# 重要參數

MAX_SENT_LENGTH = 20    # 單句最大詞數
MAX_SENTS = 100         # 單篇最多句數
MAX_NB_WORDS = 150000   # 字典上限
EMBEDDING_DIM = 100     # 詞向量維度
# 文字資料前處理

tokenizer = Tokenizer(num_words=MAX_NB_WORDS)
tokenizer.fit_on_texts(main_text_list)

# 訂好矩陣形狀 (樣本數, 單篇文章最多句數, 單句最大詞數)
data = np.zeros((len(main_text_list), MAX_SENTS, MAX_SENT_LENGTH), dtype='int32')   

# 多切掉少補零 (補在後面)
for i, sentences in enumerate(main_sentence_list):
    for j, sent in enumerate(sentences):
        if j< MAX_SENTS:
            wordTokens = text_to_word_sequence(sent)
            k=0
            for _, word in enumerate(wordTokens):
                if k<MAX_SENT_LENGTH and tokenizer.word_index[word]<MAX_NB_WORDS:
                    data[i,j,k] = tokenizer.word_index[word]
                    k=k+1                    
word_index = tokenizer.word_index

labels = to_categorical(np.asarray(cat_list))
print('data:(樣本數, 單篇最多句數, 單句最大詞數)', data.shape)
print('labels:', labels.shape)<jupyter_output>data:(樣本數, 單篇最多句數, 單句最大詞數) (9062, 100, 20)
labels: (9062, 2)<jupyter_text>切訓練、驗證、測試資料<jupyter_code>VALIDATION_SPLIT = 0.1     # 驗証資料比例
TEST_SPLIT = 0.1           # 測試資料比例
# 訓練資料 驗證資料 測試資料

p1 = int(len(data)*(1-VALIDATION_SPLIT-TEST_SPLIT))
p2 = int(len(data)*(1-TEST_SPLIT))

x_train = data[:p1]
y_train = labels[:p1]

x_val = data[p1:p2]
y_val = labels[p1:p2]

x_test = data[p2:]
y_test = labels[p2:]

print( 'train : '+str(len(x_train)) )
print( 'val : '+str(len(x_val)) )
print( 'test : '+str(len(x_test)) )
# 全零 Array，用於Attention層的非監督學習

y_train_sent_score = np.zeros((len(y_train), MAX_SENTS))
y_train_word_score = np.zeros((len(y_train), MAX_SENT_LENGTH))
y_val_sent_score = np.zeros((len(y_val), MAX_SENTS))
y_val_word_score = np.zeros((len(y_val), MAX_SENT_LENGTH))<jupyter_output><empty_output><jupyter_text>模型建構  詞向量-Word2Vec<jupyter_code># 排列順序按照Tokenizer在fit之後的詞順序，作為權重餵給 embedding Layer

# 讀取預先訓練好的word2vec模型
w2v_model = Word2Vec.load("w2v_model.model")

# 因Keras保留一層全零層所以需要加1
embedding_matrix = np.zeros((len(word_index) + 1, EMBEDDING_DIM))                    

# 將word_list對應到word2vec詞向量
for word, i in word_index.items(): 
    if str(word) in w2v_model:
        embedding_matrix[i] = np.asarray(w2v_model[str(word)],dtype='float32')       
        
# 設定word2vec為embedding層
embedding_layer = Embedding(len(word_index) + 1,                                    
                            EMBEDDING_DIM,
                            weights=[embedding_matrix],                              
                            input_length=MAX_SENT_LENGTH,
                            trainable=False)  # 已經訓練過word2vec了，因此不加入Keras訓練<jupyter_output><empty_output><jupyter_text>Attention層<jupyter_code># 定義Attention層

'''
代碼來源：https://gist.github.com/cbaziotis/6428df359af27d58078ca5ed9792bd6d
論文：https://arxiv.org/abs/1512.08756

'''

def dot_product(x, kernel):
    if K.backend() == 'tensorflow':
        # todo: check that this is correct
        return K.squeeze(K.dot(x, K.expand_dims(kernel)), axis=-1)
    else:
        return K.dot(x, kernel)

class Attention(Layer):
    def __init__(self,
                 W_regularizer=None, b_regularizer=None,
                 W_constraint=None, b_constraint=None,
                 bias=True,
                 return_attention=False,
                 **kwargs):
        self.supports_masking = True
        self.return_attention = return_attention
        self.init = initializers.get('glorot_uniform')
        self.W_regularizer = regularizers.get(W_regularizer)
        self.b_regularizer = regularizers.get(b_regularizer)
        self.W_constraint = constraints.get(W_constraint)
        self.b_constraint = constraints.get(b_constraint)
        self.bias = bias
        super(Attention, self).__init__(**kwargs)

    def build(self, input_shape):
        assert len(input_shape) == 3
        self.W = self.add_weight((input_shape[-1],),
                                 initializer=self.init,
                                 name='{}_W'.format(self.name),
                                 regularizer=self.W_regularizer,
                                 constraint=self.W_constraint)
        if self.bias:
            self.b = self.add_weight((input_shape[1],),
                                     initializer='zero',
                                     name='{}_b'.format(self.name),
                                     regularizer=self.b_regularizer,
                                     constraint=self.b_constraint)
        else:
            self.b = None
        self.built = True

    def compute_mask(self, input, input_mask=None):
        return None

    def call(self, x, mask=None):
        eij = dot_product(x, self.W)
        if self.bias:
            eij += self.b
        eij = K.tanh(eij)
        a = K.exp(eij)
        # apply mask after the exp. will be re-normalized next
        if mask is not None:
            # Cast the mask to floatX to avoid float64 upcasting in theano
            a *= K.cast(mask, K.floatx())
        # a /= K.cast(K.sum(a, axis=1, keepdims=True), K.floatx())
        a /= K.cast(K.sum(a, axis=1, keepdims=True) + K.epsilon(), K.floatx())
        weighted_input = x * K.expand_dims(a)
        result = K.sum(weighted_input, axis=1)
        if self.return_attention:
            return [result, a]
        return result

    def compute_output_shape(self, input_shape):
        if self.return_attention:
            return [(input_shape[0], input_shape[-1]),
                    (input_shape[0], input_shape[1])]
        else:
            return input_shape[0], input_shape[-1]<jupyter_output><empty_output><jupyter_text>Keras建構模型<jupyter_code># w2v_GRU_Attention

"""
模型結構：
 案件描述輸入 > 句子輸入 > word2vec詞向量 > 雙向GRU > Attention層 (產出詞重要度) > 連接層 >
                                         雙向GRU > Attention層 (產出句子重要度) > 連接層 > 判決結果
model input: 案件描述
model output: 判決結果、句子重要度

model2 input: 句子
model2 output: 詞重要度

"""

sentence_input = Input(shape=(MAX_SENT_LENGTH,), dtype='int32')
l_embedded = embedding_layer(sentence_input)
l_lstm = Bidirectional(GRU(100, dropout=0.2, return_sequences=True))(l_embedded)
l_att, word_scores = Attention(return_attention=True)(l_lstm)
l_dense = Dense(100, activation='relu')(l_att)
Word_Model = Model(sentence_input, l_dense)

verdict_input = Input(shape=(MAX_SENTS, MAX_SENT_LENGTH), dtype='int32')
verdict_encoder = TimeDistributed(Word_Model)(verdict_input)
l_lstm_sent = Bidirectional(GRU(100, dropout=0.2, return_sequences=True))(verdict_encoder)
l_att_sent, sentence_scores = Attention(return_attention=True)(l_lstm_sent)
l_dense_sent = Dense(100, activation='relu')(l_att_sent)
preds = Dense(labels.shape[1], activation='softmax')(l_dense_sent)


model = Model(verdict_input, output=(preds, sentence_scores))
model2 = Model(sentence_input, output=(word_scores))

model.compile(loss='binary_crossentropy', loss_weights=[1,0], optimizer='rmsprop', metrics=['acc'])
# 模型一 : 分類結果與句的權重
model.summary()
# 模型二 : 字的權重
model2.summary()
# FIT起來 !!!!!

model.fit(x_train, [y_train, y_train_sent_score], epochs=3, validation_data=([x_val, [y_val, y_val_sent_score]]))
# 模型測試資料判決準確率

Y_test = y_test.dot([0,1])
test_pred_label = np.argmax(model.predict(x_test)[0], axis=1)

accuracy=accuracy_score(Y_test ,test_pred_label)
print('Test Data Acc : '+str(accuracy))<jupyter_output>Test Data Acc : 0.8302094818081588<jupyter_text>輸出model結果(判決預測、句子重要度)<jupyter_code># 測試資料丟模型跑結果

y_test_pred = model.predict(x_test)

y_test_pred_label = y_test_pred[0]    # 判決結果
y_test_pred_score = y_test_pred[1]    # 句子重要度
# 測試用文章

test_victor = 150
# y_test_pred_score[test_victor]        # 測試用文章句子重要度
# y_test_pred_score[test_victor].sum()  # 權重相加要約等於1 (會有np浮點數計算誤差)
# 將數字映回文字

sentences = x_test[test_victor].tolist()
reverse_word_map = dict(map(reversed, tokenizer.word_index.items())) # 將 word_index 字典反過來

def sequence_to_text(list_of_indices):
    words = [reverse_word_map.get(letter) for letter in list_of_indices]
    return(words)
sent_list = list(map(sequence_to_text, sentences)) # 對映回來
# 整理為 dataframe

df_match_attscore = pd.DataFrame()
sent_se = pd.Series(sent_list)
df_match_attscore['句子'] = sent_se.values
weight_list = y_test_pred_score[test_victor].tolist()
weight_se = pd.Series(weight_list)
df_match_attscore['分數'] = weight_se.values
# 判決結果

y_test_pred_label[test_victor]
# 句子重要度

pd.set_option("display.max_rows",100)
df_match_attscore
# 存檔
# df_match_attscore.to_csv('案例1Attention.csv', encoding='utf_8_sig')<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.12722180329634816, 'fraction_numerical': 0.02025207368307659, 'mean_word_length': 3.304126101066296, 'pattern_counts': {'":': 0, '<': 30, '<?xml version=': 0, '>': 36, 'https://': 2, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 11, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '7', 'dir': 'jupyter-structured-clean-dedup', 'id': '429697', 'license': 'MIT', 'n_tokens_mistral': 4073, 'n_tokens_neox': 4094, 'n_words': 536, 'path': 'CFD_w2vGRUAttention.ipynb', 'repo_name': 'tommy9301122/JudgmentPrediction', 'size': '9283'}