Конвейер корпоративного машинного обучения — пример обработки

алгоритм
Конвейер корпоративного машинного обучения — пример обработки

Конвейер корпоративного машинного обучения — пример обработки


Как мы все знаем, платформа машинного обучения, которую мы обычно используем, теперь включает два модуля: автономное обучение и онлайн-оценка. Среди них автономная часть обычно отвечает заобработка данных журнала,Обработка образцов,Обработка функцийиобучение моделиЖдать. Онлайн часть включает онлайнпрогнозировать в реальном времениПроцесс (онлайн-прогноз, также известный как вывод онлайн-моделей). Блок-схема выглядит следующим образом:

В сообществе машинного обучения есть поговорка, которая считается истиной:Данные и функции определяют верхний предел машинного обучения, а модели и алгоритмы лишь приближаются к этому верхнему пределу.. Я также думаю, что это предложение очень разумно ^-^, данные имеют решающее значение для моделей машинного обучения.


существуетОбработка образцовСсылка определяет, какие журналы поведения каких пользователей используются для обучения модели (далееотбор проб), и соответствующий образецотбор пробоперация. Среди них то, какая модель обучения журнала поведения пользователя используется, напрямую определяет положительное и отрицательное соотношение выборки модели машинного обучения, а положительное и отрицательное соотношение выборки напрямую связано с реальным CTR или реальным показателем CVR оценочной модели. Как правило, мы также называем этопредыдущий CXR. В целомtrue_cxr = pos / (pos + neg); И журналы, созданные некоторыми из наших онлайн-моделей, в немного большей системе, если модель постепенно обновляется на ежедневном уровне, ежедневный объем журнала может достигать сотен миллионов или даже миллиардов, поэтому мы выполним некоторые операции выборки, в целом верноОтрицательные образцы делают отрицательную выборку. Однако, как упоминалось выше, была сделана отрицательная выборка, которая влияет на значение реального CXR, поэтому мы делаем это для соответствующего коэффициента выборки.Калибровка CXRЭксплуатация, мы подробно обсудим это ниже.

Ниже мы представим из следующих двух аспектов:

1. Выбор образца

2. Отбор проб и калибровка CXR


1. Выбор образца

Как упоминалось выше, выборка выборки определяет, какие журналы поведения пользователей модель выбирает для обучения. Конечно, мы надеемся, что выбранные образцы смогут узнать как можно больше о данных, с которыми модель сталкивается в сети.истинное распределениеРаспределение данных офлайн-обучения должно быть максимально совместимым с распределением оценочных данных онлайн.

В то же время для определения положительной и отрицательной воронки выборки мы обычно используем открытые данные о других действиях конверсии, таких как отсутствие кликов или загрузок, в качестве отрицательных выборок, а открытые данные и клики (загрузка, активация) и другие виды поведения — как отрицательные выборки. положительные образцы. Когда инженеры работают, они могутИспользовать поведение экспозиции для журнала поведения щелчка левой кнопкой мыши, определить уникальный ключ. Те, которые могут быть объединены, являются положительными образцами, в противном случае они являются положительными образцами Конкретный код представлен ниже.

Однако данные, встречающиеся на практике, очень грязные, и даже есть некоторые платежные данные, которые всеНам необходимо выполнить целенаправленную обработку с глубоким пониманием бизнес-сценариев, сгенерированных данными..

Самое простое, после открытия многих приложений на главной странице появляется первый экран. Несомненно, первый экран домашней страницы имеет огромный трафик отображения, но в то же время он также производит большое количество недействительных показов. Например:

(1)Недопустимая экспозиция. После того, как пользователь входит в приложение, он переходит сразу на другую страницу и остается на первом экране домашней страницы в течение очень короткого времени, что связано с неосознанным рабочим поведением пользователя. В реальных проектахИнженеры могут удалять действия по показу рекламы, при которых пользователи находятся на первом экране главной страницы, но не образцы кликов или других действий-конверсий.. Это всего лишь практика, насколько я знаю, в рекламной системе крупной интернет-компании среднесуточный доход онлайн ecpm увеличивается более чем на 5% за эту простую операцию.

(2)коснуться по ошибке. Пользователь случайно нажимает на определенную страницу или определенное видео, и хотя он входит, он сразу же выходит через очень короткое время. Этот вид трафика не является эффективным пользовательским поведением пользователя.Поведение очень большое и бессмысленное.В определенной степени инженеры-алгоритмы должны удалить его в соответствии с реальным бизнес-сценарием. В реальной эксплуатации,Инженеры могут получить метку времени с момента входа пользователя на страницу до момента выхода со страницы и выполнения вычитания., удалите часть сэмпла сэмпла, время входа которого слишком короткое.

(3)Уникальность отозванных образцов моделей. Многие до сих пор не понимают, какие образцы использовать для обучения модели припоминания, и по привычке копируют метод сортировки, что контрпродуктивно. если мы предположимСортировка — это искусство признаков, а припоминание — искусство образцов., особенно искусство отрицательных образцов, имеет хорошую поговорку:Отрицательные образцы — король. Сортировка очень специфична в отношении так называемых «истинно отрицательных» образцов, то есть образцы «не нажатой экспозиции» должны быть взяты как отрицательные образцы. По этой причине существует также так называемаяМетод вышеуказанного клика, то есть в качестве отрицательных образцов используются только статьи, не прошедшие клики, над статьями, на которые кликнули.. Как правило, метод вызова выборок заключается в использовании выборки кликов в качестве положительной выборки и случайной выборки в качестве отрицательной выборки. Существует даже практика ограничения горячих пользователей и горячих операций и перехвата только определенного количества выборок для каждого пользователя для обучения модели, чтобы модель отзыва могла иметь как индивидуальность, так и общность. Есть много вещей, которые можно сделать с образцами модели отзыва, вот только вкус, и я посмотрю, смогу ли я проанализировать ее подробно после процесса глубокой оптимизации.

Мы не будем здесь повторять важность образцов для модели. Далее будет представлена ​​простая версия кода. Для некоторых малых и средних интернет-компаний его можно напрямую скопировать и использовать в прошлом.У инженеров есть компании, но навыки инженеров разделяют все, хехе~

2, отбор проб и калибровка CXR

Существует много методов отбора проб, вспомните книгу, опубликованную на Huluwa.Машинное обучение на сто лицВнутри есть глава, я смутно помню, что это вроде бы глава 8, которая посвящена различным техникам выборки, таким как выборка отбраковки, выборка по важности, выборка Гиббса, выборка Монте-Карло и т.д. Тут нужны специальные споры , Студенты, которые оптимизируют отрицательную выборку, могут пойти и посмотреть. Мне стыдно, после того, как я провел время с командами по коммерциализации алгоритмов нескольких крупных интернет-компаний, я обнаружил, что случайная отрицательная выборка является самым популярным методом для всех~~~~, это то, что нужно построить ракету, но это очень легко закрутить винт.

Обычный метод случайной отрицательной выборки заключается в использовании случайной функции для генерации случайного числа.Если установлено, что текущая выборка является отрицательной выборкой, оценивается случайная книга и частота выборки, а затем выбирается выборка. Это случайная отрицательная выборка.

Для калибровки, после того как вы используете выборочные выборки для обучения модели, распределение данных обучения модели оказывается смещенным, а CXR, полученный моделью, слишком велик (поскольку вы удалили большое количество отрицательных выборок, что привело к положительным выборкам). высокий), поэтому в это время следует выполнить калибровку CXR.

Метод калибровки, обычно используемый в промышленности, представляет собой калибровочную формулу:Эта формула также является логикой для восстановления отрицательных выборок пропорционально весу оценки модели.

Эта формула взята из статьи facebook «Практические уроки прогнозирования кликов по рекламе в Facebook» в 2014 году. Если вам интересно, вы можете прочитать исходный текст.


3. Кодовый момент

talk is cheap, show the code !!!!

Теоретическая часть выборки выборки, случайной отрицательной выборки и калибровки cxr была представлена ​​выше, ниже приведен практический код, который действительно может использоваться некоторыми компаниями. Это весь код, который мне интересно писать, и мои возможности ограничены. Это семейное слово. Если вам это не нравится, не распыляйтесь~~~

Выбор образца кода и часть случайной отрицательной выборки реализованы с использованием scala spark, а часть калибровки cxr будет вставлять функцию java, подробности следующие:


@ 欢迎关注微信公众号:算法全栈之路

package Data
import org.apache.hadoop.io.compress.GzipCodec
import org.apache.spark.storage.StorageLevel
import org.apache.spark.{SparkConf, SparkContext}
import scala.util.control.Breaks

object GenerateLogSamplesWithSelectAndSample {

  case class LogItem(imei: String, timestamp: String, posid: String, triggerid: String, adid: String, event: String, label: String)

  def main(args: Array[String]): Unit = {

    val Array(logPath, outputPath, positionId, sampleRate) = args

    val sparkConf = new SparkConf().setAppName("GenerateLogSamplesWithSelectAndSample")
    val sc = new SparkContext(sparkConf)

    val positionIdStr = positionId.split(",").toSet
    val positionIdIdSet = sc.broadcast(positionIdStr)


    // 聚合得到曝光数据
    val expose_data = sc.textFile(logPath)
      .filter(x => x != null)
      .map(e => {
        val eles = e.split("\t")
        val timestamp = eles(0)
        val imei = eles(1)
        val posid = eles(2)
        val triggerid = eles(3)
        val adid = eles(4)
        val event = eles(5)
        var label = null;

        (imei, LogItem(imei, timestamp, posid, triggerid, adid, event, label))

      })
      // 过滤出固定广告为的曝光行为日志
      .filter(e => positionIdIdSet.value.contains(e._2.posid) &&  e._2.event == "expose")
      //根据 用户 imei 聚合各个用户行为
      .groupByKey()
      .mapValues(e => {
      // 根据triggerid, posid , adid 去重复
      val distinct_list = e.map(e => (e.triggerid + "$" + e.posid + "$" + e.adid, e))
        .groupBy(_._1)
        .map(e => e._2)
        .map(e => e.toArray
          .sortBy(_._2.timestamp)
          .last
          ._2
        )
      distinct_list
    })
      .persist(StorageLevel.MEMORY_AND_DISK_SER)


    // 点击行为数据
    val click_data = sc.textFile(logPath)
      .filter(x => x.nonEmpty)
      .map(e => {
        val eles = e.split("\t")
        val timestamp = eles(0)
        val imei = eles(1)
        val posid = eles(2)
        val triggerid = eles(3)
        val adid = eles(4)
        val event = eles(5)
        val label = null
        (imei, LogItem(imei, timestamp, posid, triggerid, adid, event, label))
      })
      .filter(e => positionIdIdSet.value.contains(e._2.posid) &&  e._2.event == "expose") // 过滤出点击行为
      .groupByKey()
      .persist(StorageLevel.MEMORY_AND_DISK_SER)


    // 曝光行为 left join click 行为, 能join 上的为正样本,不能join上的为负样本
    // 给样本打标签
    val labeled_data = expose_data.leftOuterJoin(click_data)
      .mapValues(e => {

        val exposeArray = e._1.toArray

        if (e._2 != null && e._2.nonEmpty) {
          val clickArray = e._2.get.toArray

          if (exposeArray.size > 0 && clickArray.size > 0) {

            for (i <- 0 to exposeArray.size - 1) {
              val expose = exposeArray(i).asInstanceOf[LogItem]
              val breaker = new Breaks
              breaker.breakable {
                for (j <- 0 to clickArray.size - 1) {

                  val exposeTriggerId = expose.triggerid
                  val exposePosId = expose.posid

                  val click: LogItem = clickArray(j).asInstanceOf[LogItem]
                  val clickTriggerId = click.triggerid
                  val clickPosId = click.posid

                  // 根据 triggerId, posId, adid 来join 曝光和点击,能join 上的 打标签为label正样本 1,否则为负样本
                  if (exposeTriggerId.equals(clickTriggerId) && !exposeTriggerId.equals("null") && !clickTriggerId.equals("null"))
                    if (exposePosId.equals(clickPosId) && !exposePosId.equals("null") && !clickPosId.equals("null"))
                      if (expose.adid != null && click.adid != null && expose.adid == click.adid) {
                        LogItem(expose.imei, expose.timestamp, expose.posid, expose.triggerid, expose.adid, null, "1")
                        breaker.break
                      }
                } // end for

              }
            } // end for
          }
        }

        // join不上的 曝光
        for (i <- 0 to exposeArray.size - 1) {
          val expose = exposeArray(i).asInstanceOf[LogItem]
          if (expose.label == null)
            LogItem(expose.imei, expose.timestamp, expose.posid, expose.triggerid, expose.adid, null, "0")
        }
        exposeArray
      })
      .flatMapValues(e => e)
      .persist(StorageLevel.MEMORY_AND_DISK_SER)


    // 以上的所有过程均为给样本打标签的过程,曝光样本 left join 点击样本的操作
    // 样本清洗流程,去掉有曝光,但是在首屏首页没有点击行为的样本的triggerid
    val noSenseTriggerIds = labeled_data.map(e => (e._2.posid + "$" + e._2.triggerid, (1, e._2.label)))
      .reduceByKey((r1, r2) => (r1._1 + r2._1, r1._2 + r2._2)) // 曝光次数,点击次数
      //一个广告位置上 , 一次triggerid 对应会有多个广告,会有对应多个曝光,聚合的是广告曝光个数和点击个数
      // 此处让首屏有10个广告, 注意 自定义修改
      .filter(x => x._2._1 <= 10 && x._2._2 == 0) // 且无点击 ,此处可以改为别的转化行为
      .map(e => e._1.split("\\$")(1))
      .collect()
      .toSet
    val noSenseTriggerIdsBC = sc.broadcast(noSenseTriggerIds)


    val final_sample_data = labeled_data
      // 样本清洗过程
      .filter(x => !noSenseTriggerIdsBC.value.contains(x._2.triggerid))
      // 负采样过程
      .map(e => {
      if (e._2.label.equals("1") || ((e._2.label.equals("0") && math.random <= sampleRate.toDouble))) {
        // 正样本全部通过,负样本 部分通过
        e
      } else null

    })
      .filter(e => e != null)
      .mapValues(e => {
        val imei = e.imei
        val triggerid = e.triggerid
        val timestamp = e.timestamp
        val adid = e.adid
        val posid = e.posid
        val label = e.label
        label +"t"+imei +"t"+triggerid +"t"+adid +"t"+posid +"t"+timestamp
      })

    val outputPartNum = math.ceil(final_sample_data.count() / 400000).toInt
    final_sample_data.repartition(outputPartNum).saveAsTextFile(outputPath,classOf[GzipCodec])
  }
}

Пример кода, приведенный выше, представляет собой практическую разработку, доступную для фильтра поведения пользователя, включающего заданный журнал позиций рекламы, метку обращения в журнале действий пользователя, простой фильтр, основанный на определенных бизнес-отрицательных выборках, и операцию отрицательной выборки. Ниже приведена калибровочная часть кода CxR:


@ 欢迎关注微信公众号:算法全栈之路

public static double ctrResetAfterSample(double probability, double samplingRate) {
        Double score = 0.0D;
        if (samplingRate > 0.0D && samplingRate < 1.0D) {
            score = probability / (probability + (1.0D - probability) / samplingRate);
        } else {
            score = probability;
        }
        return score;
    }


На этом этапе вся часть обработки образца была представлена. Вы можете использовать описанный выше метод, чтобы переписать код, который подходит вашему бизнесу. Если вы найдете его полезным, пожалуйста, поставьте лайк и поделитесь им ~

Добро пожаловать, чтобы отсканировать код и подписаться на официальный аккаунт автора: Путь к полному стеку алгоритмов.