tonglin0325的个人主页

广告系统——专业用词

1.投放相关

1.CPI (Cost per install)获客成本以及渠道是否有效的衡量指标

2.oCPM(Optimized Cost per Mille的缩写),即优化千次展现出价,本质还是按照cpm付费。采用更精准的点击率和转化率预估机制,将广告展现给最容易产生转化的用户,在获取流量的同时,提高转化率、降低转化成本,跑量提速更快。

3.oCPC(Optimized Cost per Click的缩写),即优化点击付费,本质还是按照cpc付费。采用更科学的转化率预估机制的准确性,可帮助广告主在获取更多优质流 量的同时提高转化完成率。系统会在广告主出价基础上,基于多维度、实时反馈及历史积累的海量数据,并根据预估的转化率以及竞争环境智能化的动态调整出价,进而优化广告排序,帮助广告主竞得最适合的流量,并降低转化成本。

4.oCPA(Optimized Cost per Action的缩写),即优化行为出价,本质还是按照cpa付费。当广告主在广告投放流程中选定特定的优化目标(例如:移动应用的激活,网站的下单),提供愿意为此投放目标而支付的平均价格,并及时、准确回传效果数据,我们将借助转化预估模型,实时预估每一次点击对广告主的转化价值,自动出价,最终按照点击扣费;同时,我们的转化预估模型会根据广告主的广告转化数据不断自动优化。

参考:一文详解oCPA、oCPM是什么

5.tCPA(Target Cost per Action的缩写),即目标每次转化出价。tCPA 是一种设定目标成本的出价策略,广告主设置一个目标每次转化费用,系统会努力在这个目标成本范围内优化转化。

6.oCPX(Optimized Cost per X),oCPX 是一种针对效果广告的智能出价投放方式,广告主选择明确的优化目标(如下载、激活、注册、付费),并给出期望的转化成本,系统通过机器学习预估每一次投放机会的转化概率,并结合期望成本,自动出价,保障成本效果稳定。

参考:QCon-oCPX多目标多场景联合建模在OPPO的实践

7.ROAS(Return on AD Spending,广告支出回报):ROAS 出价策略基于广告支出回报率进行优化,目标是最大化广告投资回报。ROAS = (可归因至广告的收入 / 广告成本) x 100

8.定向:在哪些流量上打广告

全文 >>

Spark学习笔记——使用PySpark

1.启动pyspark

 

2.读取文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
>>> from pyspark.sql import SparkSession
>>>
>>> spark = SparkSession.builder.appName("myjob").getOrCreate()
>>> df = spark.read.text("hdfs:///user/lintong/logs/test")
>>> df.show()
+-----+
|value|
+-----+
| 1|
| 2|
| 3|
| 4|
+-----+

  

3.退出pyspark使用exit()

 

4.使用spark-submit提交pyspark任务pi.py

1
2
spark2-submit --master local[*] /opt/cloudera/parcels/SPARK2/lib/spark2/examples/src/main/python/pi.py

全文 >>

thrift,protobuf,avro序列化对比

对比thrift使用TCompactProtocol协议,protobuf使用,以及avro使用AvroKeyOutputFormat格式进行序列化对数据进行序列化后数据量大小

由于thrift的binary数据类型不能再次序列化化成二进制,所以测试的schema中没有binary类型的字段

1.avro schema

测试数据的avro schema定义如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
{
"namespace": "com.linkedin.haivvreo",
"name": "test_serializer",
"type": "record",
"fields": [
{ "name":"string1", "type":"string" },
{ "name":"int1", "type":"int" },
{ "name":"tinyint1", "type":"int" },
{ "name":"smallint1", "type":"int" },
{ "name":"bigint1", "type":"long" },
{ "name":"boolean1", "type":"boolean" },
{ "name":"float1", "type":"float" },
{ "name":"double1", "type":"double" },
{ "name":"list1", "type":{"type":"array", "items":"string"} },
{ "name":"map1", "type":{"type":"map", "values":"int"} },
{ "name":"struct1", "type":{"type":"record", "name":"struct1_name", "fields": [
{ "name":"sInt", "type":"int" }, { "name":"sBoolean", "type":"boolean" }, { "name":"sString", "type":"string" } ] } },
{ "name":"enum1", "type":{"type":"enum", "name":"enum1_values", "symbols":["BLUE","RED", "GREEN"]} },
{ "name":"nullableint", "type":["int", "null"] }
] }

2.Thrift schema

测试数据的thrift schema定义如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
namespace java com.linkedin.haivvreo

struct struct1_name{
1: required i32 sInt;
2: required bool sBoolean;
3: required string sString;
}

enum enum1_values {
BLUE,
RED,
GREEN
}

struct union1{
1: optional double member0;
2: optional bool member1;
3: optional string member2;
}

struct test_serializer{
1: required string string1;
2: required i32 int1;
3: required i32 tinyint1;
4: required i32 smallint1;
5: required i64 bigint1;
6: required bool boolean1;
7: required double float1;
8: required double double1;
9: required list<string> list1;
10: required map<string, i32> map1;
11: required struct1_name struct1;
12: required string enum1;
13: optional i32 nullableint
}

3.protobuf schema

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
syntax = "proto3";
package com.linkedin.haivvreo;

message Struct1Name {
int32 sInt = 1;
bool sBoolean = 2;
string sString = 3;
}

enum Enum1Values
{
BLUE = 0; //proto3版本中,首成员必须为0,成员不应有相同的值
RED = 1;
GREEN = 2;
}

message TestSerializer {
string string1 = 1;
int32 int1 = 2;
int32 tinyint1 = 3;
int32 smallint1 = 4;
int64 bigint1 = 5;
bool boolean1 = 6;
double float1 = 7;
double double1 = 8;
repeated string list1 = 9;
map<string, int32> map1 = 10;
Struct1Name struct1 = 11;
Enum1Values enum1 = 12;
int32 nullableint = 13;
}

编译protobuf schema

1
protoc -I=./ --java_out=src/main/java/ ./src/main/proto3/test_serializer.proto

全文 >>

ubuntu下git安装及使用

1.设置用户名和邮箱

1
2
3
git config --global user.name "xxxx"
git config --global user.email "xxx@xxx.edu.cn"

2.查看当前git的用户和邮箱

1
2
3
git config user.name
git config user.email

3.生成秘钥,回车3下,不设置密码

1
2
ssh-keygen -t rsa -C "xxx@xxx.edu.cn" -f ~/.ssh/id_rsa_github

4. ssh目录在etc/ssh下

~/.ssh/config配置文件如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
#自己私人用的 GitHub 帳號,id_rsa 就是我自己原本用的 ssh key
Host github.com
User xxx
Hostname ssh.github.com
PreferredAuthentications publickey
IdentityFile ~/.ssh/id_rsa_github
Port 443

#公司工作用的 GitHub 帳號,此處的 COMPANY 你可以自行取名
Host gitlab.xxx.com
Hostname gitlab.xxx.com
Port xxx
User xxx
IdentityFile ~/.ssh/id_rsa

Host xx-*
HostName %h
User xxx
Port xxx

Host xxx-*
HostName %h
User xxx
Port xxx

Host xxx-*
HostName %h
User xxx
Port xxx

5.上传.pub公钥到github

6.可以git clone了

 

如何在本地使用git

http://jingyan.baidu.com/album/295430f1c62c900c7e0050fd.html?picindex=1

 

全文 >>

特征平台——feast

feast是google开源的一个特征平台,其提供特征注册管理,以及和特征存储(feature store),离线存储(offline store)和在线存储(online store)交互的SDK,官网文档:

1
2
https://docs.feast.dev/

目前最新的v0.24版本支持的离线存储:File,Snowflake,BigQuery,Redshift,Spark,PostgreSQL,Trino,AzureSynapse等,参考:

1
2
https://docs.feast.dev/reference/offline-stores

在线存储:SQLite,Snowflake,Redis,Datastore,DynamoDB,PostgreSQL,Cassandra等,参考:

1
2
https://docs.feast.dev/reference/online-stores

**provider **用于定义feast运行的环境,其提供了feature store在不同平台组件上的实现,目前有4种:local, gcp,aws和azure

provider 支持的offline store 支持的online store
local BigQuery,file Redis,Datastore,Sqlite
gcp BigQuery,file Datastore,Sqlite
aws Redshift,file DynamoDB,Sqlite
azure Mysql,file Redis,Splite

参考:

1
2
https://docs.feast.dev/getting-started/architecture-and-components/provider

**data source **用于定义特征的数据来源,每个batch data source都和一个offline store关联,比如SnowflakeSource只能和Snowflake offline store关联

data source的类型包括:file,Snowflake,bigquery,redshift,push,kafka,kinesis,spark,postgreSQL,Trino,AzureSynapse+AzureSQL

data source offline store
FileSource file
SnowflakeSource Snowflake
BigQuerySource BigQuery
RedshiftSource Redshift
PushSource(可以同时将feature写入online和offline store)  
KafkaSource(仍然处于实验性)  
KinesisSource(仍然处于实验性)  
SparkSource(支持hive和parquet文件) Spark
PostgreSQLSource PostgreSQL
TrinoSource Trino
MsSqlServerSource AzureSynapse+AzureSQL 

全文 >>

SpringMVC学习笔记——jsp基础语法

 1.JSP文件

JSP文件的命名最好采用小写的形式,比如hello.jsp,且必须加上第一句以用来指定编码,否则会出现乱码

1
2
3
4
5
6
7
8
9
10
11
12
<%@ page language="java" import="java.util.*" contentType="text/html; charset=UTF-8" %>  
<html>
<head>
<title>这是一个title</title>
</head>

<body>
<%
out.println("<h1>这是一个h1</h1>");
%>
</body>
</html>

 

2.JSP注释

显式注释  

1
2
<!-- 注释内容 -->

隐式注释,隐式注释在客户端无法看见

1
2
3
//
/* */
<% 注释内容 %>&nbsp;

3.Scriptlet(小脚本程序)

所有嵌入在HTML代码中的Java程序都必须使用Scriptlet标记起来,在JSP中一共有3种Scriptlet代码

 

全文 >>

Linux下htop的使用

linux top命令VIRT,RES,SHR,DATA的含义

 

第1行-第4行:显示CPU当前的运行负载,有几核就有几行,我的是4核

Mem:显示内存的使用情况,3887M大概是3.8G,此时的Mem不包含buffers和cached的内存,所以和free -m会不同

Swp:显示交换空间的使用情况,交换空间是当内存不够和其中有一些长期不用的数据时,ubuntu会把这些暂时放到交换空间中

 

VIRT:virtual memory usage 虚拟内存

1、进程“需要的”虚拟内存大小,包括进程使用的库、代码、数据等

2、假如进程申请100m的内存,但实际只使用了10m,那么它会增长100m,而不是实际的使用量

RES:resident memory usage 常驻内存

1、进程当前使用的内存大小,但不包括swap out

2、包含其他进程的共享

3、如果申请100m的内存,实际使用10m,它只增长10m,与VIRT相反

4、关于库占用内存的情况,它只统计加载的库文件所占内存大小

SHR:shared memory 共享内存

1、除了自身进程的共享内存,也包括其他进程的共享内存

2、虽然进程只使用了几个共享库的函数,但它包含了整个共享库的大小

3、计算某个进程所占的物理内存大小公式:RES – SHR

4、swap out后,它将会降下来

DATA

1、数据占用的内存。如果top没有显示,按f键可以显示出来。

2、真正的该程序要求的数据空间,是真正在运行中要使用的。

top 运行中可以通过 top 的内部命令对进程的显示方式进行控制。内部命令如下:

s – 改变画面更新频率

l – 关闭或开启第一部分第一行 top 信息的表示

t – 关闭或开启第一部分第二行 Tasks 和第三行 Cpus 信息的表示

m – 关闭或开启第一部分第四行 Mem 和 第五行 Swap 信息的表示

N – 以 PID 的大小的顺序排列表示进程列表

P – 以 CPU 占用率大小的顺序排列进程列表

M – 以内存占用率大小的顺序排列进程列表

h – 显示帮助

n – 设置在进程列表所显示进程的数量

q – 退出 top

s – 改变画面更新周期

序号 列名 含义

a PID 进程id

b PPID 父进程id

c RUSER Real user name

d UID 进程所有者的用户id

e USER 进程所有者的用户名

f GROUP 进程所有者的组名

g TTY 启动进程的终端名。不是从终端启动的进程则显示为 ?

h PR 优先级

i NI nice值。负值表示高优先级,正值表示低优先级

j P 最后使用的CPU,仅在多CPU环境下有意义

k %CPU 上次更新到现在的CPU时间占用百分比

l TIME 进程使用的CPU时间总计,单位秒

m TIME+ 进程使用的CPU时间总计,单位1/100秒

n %MEM 进程使用的物理内存百分比

o VIRT 进程使用的虚拟内存总量,单位kb。VIRT=SWAP+RES

p SWAP 进程使用的虚拟内存中,被换出的大小,单位kb。

q RES 进程使用的、未被换出的物理内存大小,单位kb。RES=CODE+DATA

r CODE 可执行代码占用的物理内存大小,单位kb

s DATA 可执行代码以外的部分(数据段+栈)占用的物理内存大小,单位kb

t SHR 共享内存大小,单位kb

u nFLT 页面错误次数

v nDRT 最后一次写入到现在,被修改过的页面数。

w S 进程状态。(D=不可中断的睡眠状态,R=运行,S=睡眠,T=跟踪/停止,Z=僵尸进程)

x COMMAND 命令名/命令行

y WCHAN 若该进程在睡眠,则显示睡眠中的系统函数名

z Flags 任务标志,参考 sched.h

默认情况下仅显示比较重要的 PID、USER、PR、NI、VIRT、RES、SHR、S、%CPU、%MEM、TIME+、COMMAND 列。可以通过下面的快捷键来更改显示内容。

全文 >>

Java数据结构与排序算法——堆和堆排序

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
class Node_Heap{
public int iData;

public Node_Heap(int iData) { //构造函数
super();
this.iData = iData;
}

public int getiData() {
return iData;
}

public void setiData(int iData) {
this.iData = iData;
}
}

class Heap{
private Node_Heap[] heapArray;
public int maxSize;
private int currentSize;

public Heap(int maxSize) { //构造函数
super();
this.maxSize = maxSize;
this.currentSize = 0;
heapArray = new Node_Heap[maxSize];
}

public boolean isEmpty(){
return currentSize ==0;
}

public boolean insert(int key){
if(currentSize == maxSize){
return false;
}
Node_Heap newNode = new Node_Heap(key);
heapArray[currentSize] = newNode; //把插入的节点放在最后的位置
trickleUp(currentSize++); //插入节点并把currentSize加1
return true;
}

//用于插入,把父类节点下移,然后把插入的节点放到合适的位置
public void trickleUp(int index){
int parent = (index-1)/2;
Node_Heap bottom = heapArray[index]; //暂存新插入的节点,因为需要把父节点下移
while(index>0 &amp;&amp; heapArray[parent].getiData()<bottom.getiData()){ //如果小,就下移
heapArray[index] = heapArray[parent]; //把父类节点下移
index = parent; //用于递归
parent = (parent-1)/2;
}
heapArray[index] = bottom; //把插入的节点放到合适的位置
}

public Node_Heap remove(){ //删除最大的节点
Node_Heap root = heapArray[0];
heapArray[0]=heapArray[--currentSize];
trickleDown(0);
return root;
}

//用于删除,把子类节点上移
public void trickleDown(int index){
int largerChild;
Node_Heap top = heapArray[index]; //
while(index<currentSize/2){ //如果小,就下移
int leftChild = 2*index+1;
int rightChild = leftChild+1;
if(rightChild<currentSize &amp;&amp; heapArray[leftChild].getiData() < heapArray[rightChild].getiData())
largerChild = rightChild;
else
largerChild = leftChild;
if(top.getiData()>=heapArray[largerChild].getiData())
break;
heapArray[index] = heapArray[largerChild];
index = largerChild;
}
heapArray[index] = top;
}

public void displayHeap(){
System.out.print("heapArray:");
for(int i=0;i<heapArray.length;i++){
if(heapArray[i] != null)
System.out.print(heapArray[i].getiData()+" ");
else
System.out.print(" -- ");
}
System.out.println();

int nBlanks = 32; //定义空格
int itemsPerRow = 1;
int column = 0;
int j=0; //标记当前的数组下标,从0开始
System.out.println("......................................................");
while(currentSize > 0){
if(column == 0){
for(int i=0;i<nBlanks;i++){
System.out.print(" ");
}
}
System.out.print(heapArray[j].getiData());
if(++j == currentSize){
break;
}
if(++column==itemsPerRow){ //如果每一行计数等于这一行的上限,则换行
nBlanks /= 2; //空格数减半
itemsPerRow *= 2; //每一行的上限
column = 0;
System.out.println();
}else{
for(int i=0;i<nBlanks*2-2;i++){
System.out.print(" ");
}
}
}
System.out.println("\n"+"......................................................");
}

}

public class Heap_demo {

public static void main(String[] args) {
// TODO 自动生成的方法存根
int anArrays[]={1,2,3,4,5,6,7,8,9,10};
Heap theHeap = new Heap(31);
// theHeap.insert(1);
// theHeap.insert(2);
// theHeap.insert(3);
// theHeap.insert(4);
// theHeap.insert(5);
// theHeap.insert(6);
for(int i=0;i<10;i++){
theHeap.insert(anArrays[i]);
}
theHeap.displayHeap();
//theHeap.remove();
//theHeap.displayHeap();
for(int i=0;i<10;i++){
anArrays[i]=theHeap.remove().iData;
}
for(int i=0;i<10;i++){
System.out.print(anArrays[i]+" ");
}
}

}