FastText-实践检测

文章目录

  • FastText-实践检测
  • 前言
  • 一、fasttext是什么?
  • 二、使用步骤
    • 1.引入库
    • 2.读入数据
    • 3.fasttext_demo
      • 总的练习代码
      • 效果测试
  • 总结

前言

**fasttext**是**facebook**开源的一个词向量与文本分类工具,fastText结合了自然语言处理和机器学习中最成功的理念。这些包括了使用词袋以及n-gram袋表征语句,还有使用子字(subword)信息,并通过隐藏表征在类别间共享信息;在2016年开源,典型应用场景是“带监督的文本分类问题”。提供简单而高效的文本分类和表征学习的方法,性能比肩深度学习而且速度更快。


一、fasttext是什么?

抛开那些不是很讨⼈喜欢的公式推导,来想⼀想fastText⽂本分类的核⼼思想是什么?

仔细观察模型的后半部分,即从隐含层输出到输出层输出,会发现它就是⼀个softmax线性多类别分类器,分类器的输⼊是⼀个⽤来表征当前⽂档的向量;模型的前半部分,即从输⼊层输⼊到隐含层输出部分,主要在做⼀件事情:⽣成⽤来表征⽂档的向量。那么它是如何做的呢?叠加构成这篇⽂档的所有词及n-gram的词向量,然后取平均。叠加词向量背后的思想就是传统的词袋法,即将⽂档看成⼀个由词构成的集合。于是fastText的核⼼思想就是:将整篇⽂档的词及n-gram向量叠加平均得到⽂档向量,然后使⽤⽂档向量做softmax多分类。
这中间涉及到两个技巧:字符级n-gram特征的引⼊以及分层Softmax分类。

二、使用步骤

1.引入库

代码如下(示例):

import re
import os
import pandas as pd
PATH_BASE = os.path.dirname(os.getcwd())+'\class_dataset'

2.读入数据

代码如下(示例):


def save_file():# 保存到一个文件raw_list = []for classes_dataset  in os.listdir(PATH_BASE):words_path = os.path.join(PATH_BASE, classes_dataset)for i in open(words_path, 'r', encoding='utf-8'):if len(re.findall('名称|爱心东东|京东超市|"京东国际|"\n',i)) == 1:continuelaybel = words_path.split('\\')[-1].split('-')[0]data = [i.replace('      ', '').replace(' ', '|').replace('\n', '').split('\t')[-1] +'\t'+laybel.replace('.csv','')]raw_list.append(data)pd.DataFrame(raw_list).to_csv('../updata_dataset/all_data.csv',header=None,index=0)

原始数据

先了解一下:模型搭建遵循以下步骤:

  1. 添加输⼊层(embedding层)。Embedding层的输⼊是⼀批⽂档,每个⽂档由⼀个词汇索引序列构成。例如:[10, 30, 80, 1000] 可
    能表⽰“我 昨天 来到 达观数据”这个短⽂本,其中“我”、“昨天”、“来到”、“达观数据”在词汇表中的索引分别是10、30、80、
    1000;Embedding层将每个单词映射成EMBEDDING_DIM维的向量。于是:input_shape=(BATCH_SIZE, MAX_WORDS),
    output_shape=(BATCH_SIZE,
    MAX_WORDS, EMBEDDING_DIM);
  2. 添加隐含层(投影层)。投影层对⼀个⽂档中所有单词的向量进⾏叠加平均。keras提供的GlobalAveragePooling1D类可以帮我们实现
    这个功能。这层的input_shape是Embedding层的output_shape,这层的output_shape=( BATCH_SIZE, EMBEDDING_DIM);
  3. 添加输出层(softmax层)。真实的fastText这层是Hierarchical Softmax,因为keras原⽣并没有⽀持Hierarchical Softmax,所以
    这⾥⽤Softmax代替。这层指定了CLASS_NUM,对于⼀篇⽂档,输出层会产⽣CLASS_NUM个概率值,分别表⽰此⽂档属于当前类的可
    能性。这层的output_shape=(BATCH_SIZE, CLASS_NUM)
  4. 指定损失函数、优化器类型、评价指标,编译模型。损失函数我们设置为categorical_crossentropy,它就是我们上⾯所说的softmax
    回归的损失函数;优化器我们设置为SGD,表⽰随机梯度下降优化器;评价指标选择accuracy,表⽰精度。


3.fasttext_demo

数据预处理:

"""
函数说明:去停用词
参数:content_line:文本数据sentences:存储的数据category:文本类别
"""def preprocess_text(content_line, stopwords):sentences = []for line in content_line:label = line.split('\t')[0]   # label_wordswords = line.split('\t')[1:]  # label_wordstry:# segs = jieba.lcut(words)  # 利用结巴分词进行中文分词  label_wordssegs = jieba.lcut(words[0])  # 利用结巴分词进行中文分词segs = filter(lambda x: len(x) > 1, segs)  # 去掉长度小于1的词segs = filter(lambda x: x not in stopwords, segs)  # 去掉停用词# sentences.append(label+'\t'+" ".join(segs))  # label_wordssentences.append("__label__" +str(label) + "\t" + " ".join(segs)+','+words[0].replace('\n',''))# sentences.append("__label__" +str(label) +"\t" + words[0].replace('\n',''))  #  不行太低except Exception as e:print(words)continuepd.DataFrame(sentences).to_csv('./dataset/train_words.txt', header=None, index=0)

model的构建

def train_model(ipt=None, opt=None, model='', dim=100, epoch=100, lr=0.1, loss='softmax'):# suppress: bool, 科学记数法启用# True用固定点打印浮点数符号,当前精度中的数字等于零将打印为零。# False用科学记数法;最小数绝对值是<1e-4或比率最大绝对值> 1e3。默认值Falsenp.set_printoptions(suppress=True)if os.path.isfile(model):classifier = fasttext.load_model(model)else:classifier = fasttext.train_supervised(ipt, label='"__label__', dim=dim, epoch=epoch,lr=lr, wordNgrams=2, loss=loss)"""训练一个监督模型, 返回一个模型对象@param input:           训练数据文件路径@param lr:              学习率@param dim:             向量维度@param ws:              cbow模型时使用@param epoch:           次数@param minCount:        词频阈值, 小于该值在初始化时会过滤掉@param minCountLabel:   类别阈值,类别小于该值初始化时会过滤掉@param minn:            构造subword时最小char个数@param maxn:            构造subword时最大char个数@param neg:             负采样@param wordNgrams:      n-gram个数@param loss:            损失函数类型, softmax, ns: 负采样, hs: 分层softmax@param bucket:          词扩充大小, [A, B]: A语料中包含的词向量, B不在语料中的词向量@param thread:          线程个数, 每个线程处理输入数据的一段, 0号线程负责loss输出@param lrUpdateRate:    学习率更新@param t:               负采样阈值@param label:           类别前缀@param verbose:         ??@param pretrainedVectors: 预训练的词向量文件路径, 如果word出现在文件夹中初始化不再随机@return model object"""classifier.save_model(opt)return classifier

总的练习代码


import os
import numpy as np
import fasttextfrom sklearn.metrics import recall_score, precision_score, f1_score, confusion_matrix, multilabel_confusion_matrix, \classification_reportdef train_model(ipt=None, opt=None, model='', dim=100, epoch=100, lr=0.1, loss='softmax'):# suppress: bool, 科学记数法启用# True用固定点打印浮点数符号,当前精度中的数字等于零将打印为零。# False用科学记数法;最小数绝对值是<1e-4或比率最大绝对值> 1e3。默认值Falsenp.set_printoptions(suppress=True)if os.path.isfile(model):classifier = fasttext.load_model(model)else:classifier = fasttext.train_supervised(ipt, label='"__label__', dim=dim, epoch=epoch,lr=lr, wordNgrams=2, loss=loss)"""训练一个监督模型, 返回一个模型对象@param input:           训练数据文件路径@param lr:              学习率@param dim:             向量维度@param ws:              cbow模型时使用@param epoch:           次数@param minCount:        词频阈值, 小于该值在初始化时会过滤掉@param minCountLabel:   类别阈值,类别小于该值初始化时会过滤掉@param minn:            构造subword时最小char个数@param maxn:            构造subword时最大char个数@param neg:             负采样@param wordNgrams:      n-gram个数@param loss:            损失函数类型, softmax, ns: 负采样, hs: 分层softmax@param bucket:          词扩充大小, [A, B]: A语料中包含的词向量, B不在语料中的词向量@param thread:          线程个数, 每个线程处理输入数据的一段, 0号线程负责loss输出@param lrUpdateRate:    学习率更新@param t:               负采样阈值@param label:           类别前缀@param verbose:         ??@param pretrainedVectors: 预训练的词向量文件路径, 如果word出现在文件夹中初始化不再随机@return model object"""classifier.save_model(opt)return classifierdef unified_format(data_lable, predict_lable): #  多余lable_to_cate = {'萌宠': 0, '园艺': 1, '运动_户外': 2, '星座': 3, '影视': 4, '摄影': 5, '穿搭': 6, '游戏': 7, '数码_科技': 8, '家居': 9, '汽车': 10,'户外旅游': 11, '美食': 12, '母婴': 13, '美容妆发': 14, '二次元_动漫': 15}list_uni = []list_pre = []for i, y in zip(iter(data_lable), iter(predict_lable)):unified = [lable_to_cate[i.split('__label__')[-1]] for node_i in lable_to_cate.keys() if node_i in i]predict = [lable_to_cate[y[0].split('__label__')[-1]] for node_i in lable_to_cate.keys() if node_i in y[0]]list_pre.append(predict)list_uni.append(unified)return list_uni, list_preimport jieba
import pandas as pddef getStopWords(datapath):stopwords = pd.read_csv(datapath, index_col=False, quoting=3, sep="\t", names=['stopword'], encoding='utf-8')stopwords = stopwords["stopword"].valuesreturn stopwordsdef preprocess_lcut(content_line, stopwords):sentences = []for line in content_line:words = line.split('\t')[0]  # label_wordstry:# segs = jieba.lcut(words)  # 利用结巴分词进行中文分词  label_wordssegs = jieba.lcut(line)  # 利用结巴分词进行中文分词segs = filter(lambda x: len(x) > 1, segs)  # 去掉长度小于1的词segs = filter(lambda x: x not in stopwords, segs)  # 去掉停用词# sentences.append(label+'\t'+" ".join(segs))  # label_wordssentences.append(" ".join(segs) + words)except Exception as e:print(line)continuereturn sentencesif __name__ == '__main__':dim = 300lr = 1e-3epoch = 1000# 模型存储路径# f'string' 相当于 format() 函数model = f'model/data_dim{str(dim)}_lr0{str(lr)}_iter{str(epoch)}.model'train_path = 'dataset/train_words.txt'  # 数据增强后的训练集,文章最后有提到val_path = 'dataset/val_label_sim.txt'test_path = 'dataset/test_label_sim.txt'# 输出原始标签与模型标签不匹配的文本unmatch_path = f'unmatch_classification/unmatch_classification_dim{str(dim)}_lr0{str(lr)}_iter{str(epoch)}.txt'# 模型训练classifier = train_model(ipt=train_path,opt=model,model=model,dim=dim, epoch=epoch, lr=0.5)print(classifier.words)  # list of words in dictionaryprint(classifier.labels)# # 模型测试result = classifier.test(val_path)print(result)data_words = [i.replace('\n', '').split('\t')[-1] for i inopen('./dataset/val_label_sim.txt', 'r', encoding='utf-8')]data_lable = [i.replace('\n', '').replace('"', '').split('\t')[0] for i inopen('./dataset/val_label_sim.txt', 'r', encoding='utf-8')]predict_lable = classifier.predict(data_words)[0]# 进行数据的编码list_uni, list_pre = unified_format(data_lable, predict_lable)# 降低维度list_pre = [i[0] for i in list_pre]list_uni = [i[0] for i in list_uni]print(recall_score(list_pre, list_uni, average='micro'))print(precision_score(list_uni, list_pre, average='micro'))'''average参数定义了该指标的计算方法,二分类时average参数默认是binary;多分类时,可选参数有micro、macro、weighted和samples。None:返回每个班级的分数。否则,这将确定对数据执行的平均类型。binary:仅报告由指定的类的结果pos_label。仅当targets(y_{true,pred})是二进制时才适用。micro:通过计算总真阳性,假阴性和误报来全球计算指标。也就是把所有的类放在一起算(具体到precision),然后把所有类的TP加和,再除以所有类的TP和FN的加和。因此micro方法下的precision和recall都等于accuracy。macro:计算每个标签的指标,找出它们的未加权平均值。这不会考虑标签不平衡。也就是先分别求出每个类的precision再求其算术平均。weighted:计算每个标签的指标,并找到它们的平均值,按支持加权(每个标签的真实实例数)。这会改变“宏观”以解决标签不平衡问题; 它可能导致F分数不在精确度和召回之间。samples:计算每个实例的指标,并找出它们的平均值(仅对于不同的多标记分类有意义 accuracy_score)。————————————————'''print(confusion_matrix(list_uni, list_pre))# print(multilabel_confusion_matrix(list_uni, list_pre))lable_to_cate = {'萌宠': 0, '园艺': 1, '运动_户外': 2, '星座': 3, '影视': 4, '摄影': 5, '穿搭': 6, '游戏': 7, '数码_科技': 8, '家居': 9, '汽车': 10,'户外旅游': 11, '美食': 12, '母婴': 13, '美容妆发': 14, '二次元_动漫': 15}categories = [i for i in lable_to_cate.keys()]print(classification_report(list_uni, list_pre, target_names=categories))test_demo = ['比比赞爆浆曲奇小丸子200g夹心饼干巧克力球网红小零食小吃休闲食品整箱','索讯科 GULIAN 实木床现代简约主卧双人出租房床架加宽床床经济型简易单人床 实木床40厘米高【满铺】 1.35*2米','书架简约落地简易现代客厅置物架柜家用学生卧室储物收纳 【特惠款】53×150cm-暖白-无背板','【12期免息+碎屏险】vivo手机X80新品5G手机旗舰芯片蔡司闪充旗舰机vivox80  12GB+256GB','石砾水洗米石子胶粘石头水刷石水磨石砾石黑白灰色砾石米庭院透水路面 白色磨圆 5斤','红色高跟鞋']stopwordsFile = r"./data/stopwords.txt"stopwords = getStopWords(stopwordsFile)sentence = preprocess_lcut(test_demo, stopwords)predict_lable = classifier.predict(sentence)print(predict_lable)

效果测试


数据不多 可能效果有些误差,但整体效果还是不错的,如果打不到要求建议使用ALbert

停用词网上就有:hit_stop_哈工大

———
》),
)÷(1-
”,
)、
=(
:
→
℃
&
*
一一
~~~~
’
.
『
.一
./
--
』
=″
【
[*]
}>
[⑤]]
[①D]
c]
ng昉
*
//
[
]
[②e]
[②g]
={
}
,也
‘
A
[①⑥]
[②B]
[①a]
[④a]
[①③]
[③h]
③]
1.
--
[②b]
’‘
×××
[①⑧]
0:2
=[
[⑤b]
[②c]
[④b]
[②③]
[③a]
[④c]
[①⑤]
[①⑦]
[①g]
∈[
[①⑨]
[①④]
[①c]
[②f]
[②⑧]
[②①]
[①C]
[③c]
[③g]
[②⑤]
[②②]
一.
[①h]
.数
[]
[①B]
数/
[①i]
[③e]
[①①]
[④d]
[④e]
[③b]
[⑤a]
[①A]
[②⑧]
[②⑦]
[①d]
[②j]
〕〔
][
://
′∈
[②④
[⑤e]
12%
b]
...
...................
…………………………………………………③
ZXFITL
[③F]
」
[①o]
]∧′=[
∪φ∈
′|
{-
②c
}
[③①]
R.L.
[①E]
Ψ
-[*]-
↑
.日
[②d]
[②
[②⑦]
[②②]
[③e]
[①i]
[①B]
[①h]
[①d]
[①g]
[①②]
[②a]
f]
[⑩]
a]
[①e]
[②h]
[②⑥]
[③d]
[②⑩]
e]
〉
】
元/吨
[②⑩]
2.3%
5:0
[①]
::
[②]
[③]
[④]
[⑤]
[⑥]
[⑦]
[⑧]
[⑨]
……
——
?
、
。
“
”
《
》
!
,
:
;
?
.
,
.
'
?
·
———
──
?
—
<
>
(
)
〔
〕
[
]
(
)
-
+
~
×
/
/
①
②
③
④
⑤
⑥
⑦
⑧
⑨
⑩
Ⅲ
В
"
;
#
@
γ
μ
φ
φ.
×
Δ
■
▲
sub
exp
sup
sub
Lex
#
%
&
'
+
+ξ
++
-
-β
<
<±
<Δ
<λ
<φ
<<
=
=
=☆
=-
>
>λ
_
~±
~+
[⑤f]
[⑤d]
[②i]
≈
[②G]
[①f]
LI
㈧
[-
......
〉
[③⑩]
第二
一番
一直
一个
一些
许多
种
有的是
也就是说
末##末
啊
阿
哎
哎呀
哎哟
唉
俺
俺们
按
按照
吧
吧哒
把
罢了
被
本
本着
比
比方
比如
鄙人
彼
彼此
边
别
别的
别说
并
并且
不比
不成
不单
不但
不独
不管
不光
不过
不仅
不拘
不论
不怕
不然
不如
不特
不惟
不问
不只
朝
朝着
趁
趁着
乘
冲
除
除此之外
除非
除了
此
此间
此外
从
从而
打
待
但
但是
当
当着
到
得
的
的话
等
等等
地
第
叮咚
对
对于
多
多少
而
而况
而且
而是
而外
而言
而已
尔后
反过来
反过来说
反之
非但
非徒
否则
嘎
嘎登
该
赶
个
各
各个
各位
各种
各自
给
根据
跟
故
故此
固然
关于
管
归
果然
果真
过
哈
哈哈
呵
和
何
何处
何况
何时
嘿
哼
哼唷
呼哧
乎
哗
还是
还有
换句话说
换言之
或
或是
或者
极了
及
及其
及至
即
即便
即或
即令
即若
即使
几
几时
己
既
既然
既是
继而
加之
假如
假若
假使
鉴于
将
较
较之
叫
接着
结果
借
紧接着
进而
尽
尽管
经
经过
就
就是
就是说
据
具体地说
具体说来
开始
开外
靠
咳
可
可见
可是
可以
况且
啦
来
来着
离
例如
哩
连
连同
两者
了
临
另
另外
另一方面
论
嘛
吗
慢说
漫说
冒
么
每
每当
们
莫若
某
某个
某些
拿
哪
哪边
哪儿
哪个
哪里
哪年
哪怕
哪天
哪些
哪样
那
那边
那儿
那个
那会儿
那里
那么
那么些
那么样
那时
那些
那样
乃
乃至
呢
能
你
你们
您
宁
宁可
宁肯
宁愿
哦
呕
啪达
旁人
呸
凭
凭借
其
其次
其二
其他
其它
其一
其余
其中
起
起见
起见
岂但
恰恰相反
前后
前者
且
然而
然后
然则
让
人家
任
任何
任凭
如
如此
如果
如何
如其
如若
如上所述
若
若非
若是
啥
上下
尚且
设若
设使
甚而
甚么
甚至
省得
时候
什么
什么样
使得
是
是的
首先
谁
谁知
顺
顺着
似的
虽
虽然
虽说
虽则
随
随着
所
所以
他
他们
他人
它
它们
她
她们
倘
倘或
倘然
倘若
倘使
腾
替
通过
同
同时
哇
万一
往
望
为
为何
为了
为什么
为着
喂
嗡嗡
我
我们
呜
呜呼
乌乎
无论
无宁
毋宁
嘻
吓
相对而言
像
向
向着
嘘
呀
焉
沿
沿着
要
要不
要不然
要不是
要么
要是
也
也罢
也好
一
一般
一旦
一方面
一来
一切
一样
一则
依
依照
矣
以
以便
以及
以免
以至
以至于
以致
抑或
因
因此
因而
因为
哟
用
由
由此可见
由于
有
有的
有关
有些
又
于
于是
于是乎
与
与此同时
与否
与其
越是
云云
哉
再说
再者
在
在下
咱
咱们
则
怎
怎么
怎么办
怎么样
怎样
咋
照
照着
者
这
这边
这儿
这个
这会儿
这就是说
这里
这么
这么点儿
这么些
这么样
这时
这些
这样
正如
吱
之
之类
之所以
之一
只是
只限
只要
只有
至
至于
诸位
着
着呢
自
自从
自个儿
自各儿
自己
自家
自身
综上所述
总的来看
总的来说
总的说来
总而言之
总之
纵
纵令
纵然
纵使
遵照
作为
兮
呃
呗
咚
咦
喏
啐
喔唷
嗬
嗯
嗳

总结

提示:以上是了解tasttext做的小验证

【FastText——总结笔记】相关推荐

  1. 文本分类论文阅读笔记

    文章目录 CNN系列 Effective Use of Word Order for Text Categorization with Convolutional Neural Networks A ...

  2. NLP︱高级词向量表达(二)——FastText(简述、学习笔记)

    FastText是Facebook开发的一款快速文本分类器,提供简单而高效的文本分类和表征学习的方法,不过这个项目其实是有两部分组成的,一部分是这篇文章介绍的 fastText 文本分类(paper: ...

  3. fasttext 安装_fasttext使用笔记

    http://blog.csdn.net/m0_37306360/article/details/72832606 这里记录使用fastText训练word vector笔记 下载到本机: $ git ...

  4. 学习笔记四:word2vec和fasttext

    FastText:快速的文本分类器 文章目录 一.word2vec 1.1 word2vec为什么 不用现成的DNN模型 1.2 word2vec两种模型:CBOW和Skip-gram 1.2 wor ...

  5. NLP学习笔记-FastText文本分类(四)

    分类的目的和分类的方法 1. 文本分类的目的 回顾之前的流程,我们可以发现文本分类的目的就是为了进行意图识别 在当前我们的项目的下,我们只有两种意图需要被识别出来,所以对应的是2分类的问题 可以想象, ...

  6. cs224n学习笔记 03:Subword Models(fasttext附代码)

    课程内容 语言学的一点小知识 词级字符级模型 n-gram思想 FastText模型 1 .人类语言声音:语音学和音系学 语音学是音流,这是属于物理层面的东西 词法学:一个n-grams的代替方案 在 ...

  7. 【NLP】fastText词向量与文本分类工具

    一.简介 fastText 是 Facebook 于2016年开源的一个词向量训练与文本分类工具,其典型应用场景是"无监督的词向量学习"和"有监督的文本分类". ...

  8. 【NLP】NLP重铸篇之Fasttext

    文本分类 论文标题:Bag of Tricks for Efficient Text Classification 论文链接:https://arxiv.org/pdf/1607.01759.pdf ...

  9. Word Embedding Papers | 经典再读之fastText

    关于作者:张正,坐标巴黎,上班NLP,下班词嵌入. 从词嵌入到句嵌入 到底什么是 fastText ? 先说结论,fastText 在不同语境中至少有两个含义: 1. 在文章 Bag of Trick ...

最新文章

  1. Linux文件实时同步--inotify + rsync + pyinotify
  2. 【翻译】Programming Ruby——正则表达式
  3. jstl 处理字符串函数 substring spli等
  4. php连接mysql总结_php连接数据库的三种方式的总结
  5. 游戏建模、纹理、后期,手把手教你制作《向日葵公主》
  6. 计算机网络系统容错检测,计算机系统的容错技术方法
  7. Python中的自定义进程和进程池
  8. ES6公用分页组件的封装及应用举例
  9. 按键精灵手机助手学习过程中的教程集锦收藏
  10. ARINC 429总线学习资料?
  11. 第十四届全国大学生信息安全竞赛部分wp
  12. mac idea向上/向下插入空行快捷键
  13. analyze怎么优化oracle,Analyze table对Oracle性能的提升
  14. [日推荐]『车主码』解决临时停车、请人挪车的小麻烦
  15. 成为一棵大树必备的6个条件
  16. 浏览器访问Linux的Tomcat
  17. incsgo 可直接立刻取回皮肤的CSGO饰品皮肤开箱网站
  18. 明明是那么好的人,却又是那么伤人的人
  19. 解决问题“The App ID ‘XXXXX‘ appears to be in use by the App Store, so it can not be removed at this ti”
  20. 小篮子玩意儿、你苏爷就是扣字神话不服气么。

热门文章

  1. 『WX运动』想上封面?就你那点步数怎么能行?点进来!老铁,借一步说话
  2. 服务器windows server 2019 系统图解安装
  3. 全国计算机等级考试忘了准考证号码怎么查询
  4. 绘画教程:鼻子怎么画?人物的五官怎么画?
  5. 英文PRESENTATION高手必备的常用表达,终于知道如何像乔布斯一样用灵魂和感情演讲了~
  6. 解决“jupyter导出图片怎么是空白的”
  7. echarts 柱状图设置边框_echarts 如何设置提示框边框阴影
  8. 浏览器打开只能在微信或支付宝打开的链接(喂饭教程 小白可看)
  9. 简单工厂模式和工厂方法模式
  10. 短边翻转一张A4纸打印18页PPT