LDA主题提取train and predict

# Autor cf
#!/usr/bin/env Python
# coding=utf-8

'''
1、从csv或xlsx中读数据
2、使用sklearn库
'''


import pyLDAvis.sklearn
import pyLDAvis
import numpy as np
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.decomposition import LatentDirichletAllocation
import pandas as pd
import jieba
import re
import os


# 本地 csv 文件,可以是本地文件,也可以是远程文件
source_path = '舆情.xlsx'
# 文本 csv 文件里面文本所处的列名,注意这里一定要填对,要不然会报错的!
document_column_name = '文本'
# 输出主题词的文件路径
top_words_csv_path = 'top-topic-words.csv'
# 输出各文档所属主题的文件路径
predict_topic_csv_path = 'document-distribution.csv'
# 可视化 html 文件路径
html_path = 'document-lda-visualization-top.html'
# 选定的主题数
n_topics = 3
# 要输出的每个主题的前 n_top_words 个主题词数
n_top_words = 20
# 去除无意义字符的正则表达式
pattern = u'[\\s\\d,.<>/?:;\'\"[\\]{}()\\|~!\t"@#$%^&*\\-_=+a-zA-Z,。\n《》、?:;“”‘’{}【】()…¥!—┄-]+'
url = 'https://raw.githubusercontents.com/Micro-sheep/Share/main/zhihu/answers.csv'


def top_words_data_frame(model: LatentDirichletAllocation,
                         tf_idf_vectorizer: TfidfVectorizer,
                         n_top_words: int) -> pd.DataFrame:
    '''
    求出每个主题的前 n_top_words 个词

    Parameters
    ----------
    model : sklearn 的 LatentDirichletAllocation
    tf_idf_vectorizer : sklearn 的 TfidfVectorizer
    n_top_words :前 n_top_words 个主题词

    Return
    ------
    DataFrame: 包含主题词分布情况
    '''
    rows = []
    feature_names = tf_idf_vectorizer.get_feature_names()
    for topic in model.components_:
        top_words = [feature_names[i]
                     for i in topic.argsort()[:-n_top_words - 1:-1]]
        rows.append(top_words)
    columns = [f'topic {i+1}' for i in range(n_top_words)]
    df = pd.DataFrame(rows, columns=columns)

    return df


def predict_to_data_frame(model: LatentDirichletAllocation, X: np.ndarray) -> pd.DataFrame:
    '''
    求出文档主题概率分布情况

    Parameters
    ----------
    model : sklearn 的 LatentDirichletAllocation
    X : 词向量矩阵

    Return
    ------
    DataFrame: 包含主题词分布情况
    '''
    matrix = model.transform(X)
    columns = [f'P(topic word {i+1})' for i in range(len(model.components_))]
    df = pd.DataFrame(matrix, columns=columns)
    return df



def predict_new_text(text_to_predict_list):
    #################################### 1、加载训练好的LDA和tf-idf
    lda = joblib.load('lda-joblib.pkl')
	tf_idf_vectorizer = joblib.load('tf_idf_vectorizer.pkl')

	# features = tf_idf_vectorizer.get_feature_names()
	# print(features)
	# print(len(features))

	# todo 模型预测:在主题-词的概率分布不变的前提下,推理文档-主题概率分布
	################################### 2、文本预处理:切词
	# 文本先预处理,再在词频模型中结构化,然后将结构化的文本list传入LDA主题模型,判断主题分布。
	cuts = []
	for text in text_to_predict_list:
	    cut_text = [' '.join(jieba.lcut(text))]
	    cuts += cut_text
	print(cuts)

	###################################  3、把文字转tf-idf向量表示
	text_tf_idf = tf_idf_vectorizer.transform(cuts).toarray()
	print(text_tf_idf.shape)
	print(text_tf_idf)

	# 打印非零元素index
	nonzero = np.nonzero(text_tf_idf)
	print(nonzero)
	print(np.array(nonzero).ndim)


	################################### 4、计算要预测的每个文本的主题概率分布情况
	text_predict_df = predict_to_data_frame(lda, text_tf_idf)
	print('预测文本的主题概率分布情况:')
	print(text_predict_df)


#################################### 0、文件预处理
if not os.path.exists(source_path):
    print(f'请用浏览器打开 {url} 并下载该文件(如果没有自动下载,则可以在浏览器中按键盘快捷键 ctrl s 来启动下载)')
    os.exit()
df = (
    pd.read_excel(source_path)
    .drop_duplicates()
    .rename(columns={
        document_column_name: 'text'
    }))

# 去重、去缺失、分词
df['cut'] = (
    df['text']
    .apply(lambda x: str(x))
    .apply(lambda x: re.sub(pattern, ' ', x))
    .apply(lambda x: " ".join(jieba.lcut(x)))
)


#################################### 1、构造 tf-idf
tf_idf_vectorizer = TfidfVectorizer()
tf_idf = tf_idf_vectorizer.fit_transform(df['cut'])

lda = LatentDirichletAllocation(
    n_components=n_topics,
    max_iter=50,
    learning_method='online',
    learning_offset=50,
    random_state=0)

#################################### 2、使用 tf_idf 语料训练 lda 模型
lda.fit(tf_idf)

#################################### 3、计算 n_top_words 个主题词
top_words_df = top_words_data_frame(lda, tf_idf_vectorizer, n_top_words)

# 保存 n_top_words 个主题词到 csv 文件中
top_words_df.to_csv(top_words_csv_path, encoding='utf-8-sig', index=None)

# 转 tf_idf 为数组,以便后面使用它来对文本主题概率分布进行计算
X = tf_idf.toarray()

#################################### 4、计算完毕主题概率分布情况
predict_df = predict_to_data_frame(lda, X)

# 保存文本主题概率分布到 csv 文件中
predict_df.to_csv(predict_topic_csv_path, encoding='utf-8-sig', index=None)


#################################### 5、模型 + 分词向量 保存
from sklearn.externals import joblib
joblib.dump(lda, 'lda-joblib.pkl')
joblib.dump(tf_idf_vectorizer, 'tf_idf_vectorizer.pkl')


# todo 模型预测:在主题-词的概率分布不变的前提下,推理文档-主题概率分布
#################################### 6、新文本预测
# 文本先预处理,再在词频模型中结构化,然后将结构化的文本list传入LDA主题模型,判断主题分布。
text_to_predict_list = ['称交警(016636)因我骑电动车没有走人行道暴力执法,用手拉我,将我按在花坛旁边,报警人不满,请交警核实处理。']
predict_new_text(text_to_predict_list )

#################################### 7、使用 pyLDAvis 进行可视化
data = pyLDAvis.sklearn.prepare(lda, tf_idf, tf_idf_vectorizer)
pyLDAvis.save_html(data, html_path)
# 清屏
os.system('clear')
# 浏览器打开 html 文件以查看可视化结果
os.system(f'start {html_path}')

print('本次生成了文件:',
      top_words_csv_path,
      predict_topic_csv_path,
      html_path)

#################################### 8、打印模型的收敛效果
print(lda.perplexity(tf_idf))  # 收敛效果

你可能感兴趣的:(算法学习,python,sklearn)