机器学习通俗易懂系列之trainingData

本文介绍如何通过规则筛选、迭代模型、聚类及clickGraph等方法自动标注数据,解决监督学习中训练集不足的问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1.写在前面

做Machine Learning的同学都很熟悉,很多时候主要还是有监督学习,不能跳过训练集这个问题。

有了训练集,接下来实验各种模型(Google + Github 玩转所有主流模型了解一下…),选个效率和效果适合的model,改改结构,调调参啥的就ok。

实际上,最麻烦的应该是训练集和特征工程,后者或许因为DeepLearning的出现被弱化了一些,但在很多领域还是头号种子地位。

这一篇,说说最基础的,Training Data/Labeled Data获取,没这个,你玩个球?

你不可能全靠人去大量给你标注训练集,因为时间成本+金钱成本。
为啥有bert/XLnet这种玩意儿出现,就是因为有监督的弱点太明显。

so,你是做算法的,请用你的智慧去给data打上你要的label,谢谢!

以下主要经验来源于文本分类,但可迁移许多场景,好记性不如烂笔头。

2.数据打上label

2.1 规则筛选+迭代模型

1.应该是使用最频繁、最有效的方式;

2.举个例子:
有一堆文本,现在让你给其中影视相关的打上“影视”这个label,去爬下主流的比如aiqiyi、TencentVideo的影视专名,根据命中专名的频率等信息来构建规则筛选样本

3.迭代弱模型:
然后你可以训练个弱分类器对剩余样本进行label,第一阶段review置信度高的data,第二阶段review边界部分样本,以此迭代分类器,最终模型标注所有的data,此谓Active Learning

4.update:
建议走迭代弱模型之前,多做一个操作,即规则自增/关键特征自增,仍然举例子,对于操作1获得的data,可以通过类似tf-idf的思想设计算法筛选能够代表该正样本集合的特征,例如可筛选出“剧情”,“多少集”这种的字段,此时规则进行了扩充,又可以额外标注样本。
这个步骤最好有个测试集,训练模型来判断自增加的规则是否有效。

这种方式对文本数据、结构化数据都适用。

2.2 聚类+标注样本?

1.也挺常见,但是因为需要特征向量,所以稍微麻烦一些,具体的聚类算法以及分布式使用后续再说;

2.特征向量的做法:对于文本有TF-IDF,LDA主题模型,预训练模型的向量提取如BERT提取、word2vec词向量求和/平均转化为文本向量等等,对于结构化数据有特征工程后的产物(可能需要PCA/SVD);

3.向量记得比较下是否归一化更好;

4.聚类的簇多一些好;

5.簇的label可以依据上一种规则筛选方式的Labeled Data确认,比如使用出现频率最大的label作为该簇的label,值得注意的是这种方法需要考虑样本分布比例,举个例子:正样本与负样本真实比例为1:10(大致估计,或者少量抽样标注);那么一个簇中,Labeled Data正负样本比例为4:6,尽管负样本多,但是可能说这个簇更偏向于正样本而不是负样本。

这里有一个要注意的,向量是需要自己想办法构建的,除了前面的特征工程、文本的vector,构建行为图然后node2vec也是一个好路子,比如用户的商品购买行为、连续搜索行为、评论行为…

2.3 clickGraph/co-click挖掘

1.这个原理很简单,举个例子,很多影视query,点击集中于某几个网站如 www.aiqiyi.com,那么反过来,点击这些网站相关的可以认为是影视query;

2.clickGraph这是一个论文,自己去搜下,原理是可以理解为协同过滤(可以看做矩阵分解)+ 部分labeled Data有监督loss最小化

3.注意截断url,这里就有说法了,一级、二级还是三级,看实际情况;

4.可以举一反三到其他领域;

5.代码如下,主要是一些矩阵操作,可以用spark自带的,我因为具体情况需要自己也写了,大致复现论文,供参考,抛砖引玉。

package F_DistribClickGraph

import org.apache.spark.sql.DataFrame
import org.apache.spark.sql.functions.{col, lit, sum}

import scala.collection.mutable
import scala.collection.mutable.ListBuffer

object F_Utils {
    //========================自定义矩阵的各种运算,可以看看===================
    //query url 生成CoordinateMatrix矩阵,这个矩阵要求行列索引为Long型,麻烦
    //自定义一个索引为任意类型可以满足的矩阵运算函数,满足坐标矩阵CoordinateMatrix的形式
    def matrix_multi(dataFrame1: DataFrame,dataFrame2: DataFrame):DataFrame = {
        val d1_row_name = dataFrame1.columns(0)
        val d1_col_name = dataFrame1.columns(1)
        val d1_value_name = dataFrame1.columns(2)
        val d2_row_name = dataFrame2.columns(0)
        val d2_col_name = dataFrame2.columns(1)
        val d2_value_name = dataFrame2.columns(2)

        val mergeDF = dataFrame1.join(dataFrame2,dataFrame1.col(d1_col_name) === dataFrame2(d2_row_name))
        val predictDF = mergeDF.
          withColumn("value_multi", col(d1_value_name)*col(d2_value_name)).
          drop(d1_col_name,d2_row_name,d1_value_name,d2_value_name).
          groupBy(d1_row_name,d2_col_name).
          agg(sum("value_multi") as "value_multi_new")
        predictDF
    }

    //矩阵转置
    //df.select(df.columns(1),df.columns(0),df.columns(2))
    def matrix_trans(df:DataFrame):DataFrame={
        df.select(col(df.columns(1)) as df.columns(1)+"row_T",
            col(df.columns(0)) as df.columns(0)+"column_T",
            col(df.columns(2)) as df.columns(2)+"value_T")
    }

    //矩阵数乘
    def matrix_num(df:DataFrame,num:Float):DataFrame={
        df.withColumn(df.columns(2),lit(num) * col(df.columns(2)))
    }

    //矩阵加减,把行列名做成相同的,外面withColumn
    //多列key来join,两种写法,注意加减这里,不能是inner join 否则A矩阵有的元素在B的位置没有就会被抛弃,这和矩阵相乘不同
    //val Lead_all = Leads.join(Utm_Master,Leaddetails.columns("LeadSource","Utm_Source","Utm_Medium","Utm_Campaign") ==Utm_Master.columns("LeadSource","Utm_Source","Utm_Medium","Utm_Campaign"),"left")
    def matrix_add(dataFrame1: DataFrame,dataFrame2: DataFrame,tag:String):DataFrame = {
        val d1_row_name = dataFrame1.columns(0)
        val d1_col_name = dataFrame1.columns(1)
        val mergeDF = dataFrame1.join(dataFrame2,Seq(d1_row_name,d1_col_name),"fullouter").na.fill(0.0)
        var predictDF = mergeDF.withColumn("value_add",col(dataFrame1.columns(2))+col(dataFrame2.columns(2)))
        if(tag == "-"){
            predictDF = mergeDF.withColumn("value_add",col(dataFrame1.columns(2))-col(dataFrame2.columns(2)))
        }
        predictDF.select(d1_row_name,d1_col_name,"value_add")
    }

    //矩阵value之和
    def value_sum(df:DataFrame):Double = {
        df.agg(sum(df.columns(2))).first.getDouble(0)
    }

}
val pow_udf = udf[Float, Float](value_float => pow(value_float.toDouble,-0.5).toFloat)
//W矩阵,D矩阵
//=================================clickGraph算法===================================
val matrix_W = spark.read.parquet("hdfs://xxxx/url_filter_toCGA_W")
val matrix_WWT = matrix_multi(matrix_W,matrix_trans(matrix_W))
matrix_WWT.repartition(60).write.parquet("hdfs://xxxx/matrix_wWT_allCls")
val matrix_D_ne_1 = matrix_WWT.groupBy("query").
      agg(sum("value_multi_new") as "value_multi_new_sum").
      drop("value_multi_new").
      withColumn("pow_1_2",pow_udf(col("value_multi_new_sum"))).
      withColumn("query_T",col("query")).
      withColumnRenamed("query","query_D").
      select("query_D","query_T","pow_1_2")
matrix_D_ne_1.repartition(60).write.parquet("hdfs://xxxx/matrix_D_allCls")
//B矩阵,query url value_matrixB
val matrix_B_1 = matrix_multi(matrix_D_ne_1,matrix_W).
      withColumnRenamed("value_multi_new","value_matrixB").
      withColumnRenamed("query_D","query")
matrix_B_1.repartition(60).write.parquet(hdfs://xxxx/matrix_B_allCls")

val matrix_B = spark.read.parquet("hdfs://xxxx/matrix_B_allCls")
val alpha = 0.6.toFloat

//F矩阵, query,cls1,count_cls_rela
//H矩阵迭代,url cls1 value_multi_new
val F_init = spark.read.parquet("hdfs://xxxx/url_filter_F")
var matrix_F = F_init
var matrix_H = matrix_multi(matrix_trans(matrix_B),matrix_F)
F_init.write.parquet("hdfs://xxxx/url_filter_F0")

for(i <- 1 until 10){
    matrix_F = spark.read.parquet("hdfs://xxxx/url_filter_F"+(i-1))
    matrix_H = matrix_multi(matrix_trans(matrix_B),matrix_F)
    //println(value_sum(matrix_H))
    matrix_F = matrix_add(
        matrix_num(matrix_multi(matrix_B,matrix_H),alpha),
        matrix_num(F_init,1.0.toFloat-alpha),
        "+"
        ).withColumnRenamed("value_add","count_cls_rela")
    //query cls1 value_multi_new + query,cls1,count_cls_rela  ->query_D cls1 value_add
    println(value_sum(matrix_F))
    println(matrix_F.count())
    HdfsFileUtils.deleteFile("hdfs://xxxx/url_filter_F"+i)
    matrix_F.repartition(60).write.parquet("hdfs://xxxx/url_filter_F"+i)
        }

2.4 tri-training

利用少量标注样本扩招,具体看周老师的论文吧

3.训练集制作+小结

3.1 样本比例优化

不是data打上label之后就是训练集了,还存在分布比例平衡的问题,可以根据测试集上的表现来优化,比如某个类别A大量误召其他类别B、C、D、E…,且类别上数量分散,很有可能是训练集中A比例过高

3.2 样本label清洗

当测试集上的错误和3.1不一样,是某两类的互相误召,那可能是边界样本标签问题,可利用前面样本扩招的方式定位这些bad Case的相似样本进行清洗

3.3 训练集有了,happy model吧

所谓大数据时代,重要的就是数据,现在都快被炒烂的AI、DL等等很是光鲜,但实际解决问题时,数据尤其labeled数据,可是很珍贵的,毕竟这些github不会给你,需要你熟练使用各种算法+数据敏感性去挖掘。

### 机器学习算法原理及其图表可视化 #### 背景介绍 机器学习是一门多领域交叉学科,涉及概率论、统计学、逼近论、凸分析、算法复杂度理论等多门学科[^1]。其核心目标是从数据中提取模式并用于预测或决策。为了更好地理解机器学习的工作机制,可以通过图表形式直观展示其基本流程。 --- #### 机器学习算法的核心原理 机器学习的主要过程可以分为以下几个阶段: 1. **数据收集与预处理** 数据是机器学习的基础,通常需要经过清洗、去噪、标准化等一系列操作才能被模型有效利用[^2]。 2. **特征工程** 特征的选择和提取直接影响到模型性能的好坏。这一环节可能包括降维、编码转换以及特征选择等技术[^3]。 3. **模型训练** 使用特定的算法(如线性回归Linear Regression, 支持向量机SVM等)对已标注的数据集进行拟合,调整参数直至达到最优解[^4]。 4. **评估与验证** 利用测试集合衡量所建立模型的效果好坏,并据此改进策略或者重新挑选更合适的算法类型。 5. **部署应用** 将最终确认下来的版本投入生产环境当中执行在线推理任务。 上述各步骤构成了完整的生命周期框架图如下所示: ```plaintext +-------------------+ | Data Input | +---------+---------+ | v +---------+---------+ |Data Preprocessing| +---------+---------+ | v +---------+---------+ | Feature Engineering| +---------+---------+ | v +---------+---------+ | Model Training & Tuning| +---------+---------+ | v +---------+---------+ |Evaluation and Validation| +---------+---------+ | v +---------+---------+ | Deployment to Production| +-----------------------+ ``` 此结构化视图有助于初学者快速掌握整体思路走向;同时每一步骤内部还存在更加细化的内容分支可供深入研究探讨。 --- #### 可视化的意义及方法 对于抽象概念的理解而言,图形表达往往比文字描述更为高效直接。因此,在教学过程中引入恰当类型的图表显得尤为重要。以下是几种常见的表示方式: - 流程框图:清晰展现各个组成部分之间的逻辑关系; - 散点分布图:适用于观察样本间距离远近情况以便选取KNN邻居数量; - 曲面响应图:适合描绘高维度空间里损失函数随权值变化趋势曲线走势特点等等... 具体实现上可借助Matplotlib库完成基础绘图工作,Seaborn则提供了更高层次封装简化样式定制难度。下面给出一段简单示例代码片段演示如何绘制二维平面内的分类边界线条图案效果。 ```python import numpy as np import matplotlib.pyplot as plt from sklearn import datasets from sklearn.linear_model import LogisticRegression # 加载鸢尾花数据集 iris = datasets.load_iris() X = iris.data[:, :2] # 我们仅取前两个特征便于作图 Y = iris.target logreg = LogisticRegression(max_iter=1000) # 训练模型 logreg.fit(X, Y) # 绘制决策边界 x_min, x_max = X[:, 0].min() - .5, X[:, 0].max() + .5 y_min, y_max = X[:, 1].min() - .5, X[:, 1].max() + .5 h = .02 # 步长设置 xx, yy = np.meshgrid(np.arange(x_min, x_max, h), np.arange(y_min, y_max, h)) Z = logreg.predict(np.c_[xx.ravel(), yy.ravel()]) Z = Z.reshape(xx.shape) plt.figure(1, figsize=(8, 6)) plt.pcolormesh(xx, yy, Z, cmap=plt.cm.Paired) # 绘制散点图 plt.scatter(X[:, 0], X[:, 1], c=Y, edgecolors='k', cmap=plt.cm.Paired) plt.xlabel('Sepal length') plt.ylabel('Sepal width') plt.xlim(xx.min(), xx.max()) plt.ylim(yy.min(), yy.max()) plt.xticks(()) plt.yticks(()) plt.show() ``` 以上脚本运行后会生成一幅色彩分明区域划分的地图形象地反映了不同类别之间界限所在位置信息. --- #### 总结 综上所述,通过对机器学习算法原理的学习结合适当工具辅助下的视觉呈现手段可以使原本晦涩难懂的知识变得通俗易懂起来。这不仅有利于提高学生兴趣程度而且还能促进他们更快捷准确地把握住重点难点之处从而为进一步探索打下坚实基础。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值