目录

  • 特征工程
    • 缺失值处理
    • 特征归一化
    • 连续特征离散化
    • 离散特征one-hot编码
    • ID特征Embedding
    • 特征构造方法
    • AUC指标补充
  • 实验:LR预估CTR

特征工程

缺失值处理

pandas 是为了解决数据分析任务而创建的,并考虑了缺失值的处理。在 pandas 中可以使用如下代码进行缺失值的判断:

>>> import pandas as pd
# 创建一个Series
>>> s = pd.Series([1,2,3,None,5])
# 输出s的缺失值数量
>>> s.isna().sum()
1
# 创建一个DataFrame
>>> df = pd.DataFrame({'a':[1,2,3,None,5],'b':[None]*4+[5],'c':[2,3,4,5,6]})
# 输出每一列的缺失值数量
>>> df.isna().sum()
a    1
b    4
c    0
# dtype: int64

None表示人为指定的缺失值,也可以用 numpy 中的 nan 来替代,即:np.nan

缺失值删除
当 dataframe 中的某列缺失值很多时,可以通过drop()方法将这列数据全部删除,以上面的 df 为例,删除 b 列数据的代码如下:

# 删除 b 列,返回新数据
>>> df.drop('b',axis=1)a     c
0 1.0     2
1 2.0     3
2 3.0     4
3 NaN   5
4 5.0     6
# 删除 b 列,inplace操作修改原数据
>>> df.drop('b',axis=1,inplace=True)

另外还有一个df.dropna(axis=0, inplace=False)方法,它会将含有缺失值的行或列全部删除,使用方法如下:

import pandas as pddf = pd.DataFrame({'a':[1,2,3,None,5],'b':[None]*4+[5],'c':[2,3,4,5,6]})
df

# 删除含有缺失值的行,返回新数据
dfnew=df.dropna(axis=0)
dfnew

# 删除含有缺失值的列,返回新数据
dfnew=df.dropna(axis=1)
dfnew


缺失值填充
通过fillna()方法可以填充缺失值,主要有两类填充方式,分别对应该方法的value参数和method参数:

>>> import pandas as pd# 创建一个Series
>>> s = pd.Series([4,1,3,None,4,1,5])# 1. value参数:使用指定值填充# 使用常数值填充
>>> s1 = s.fillna(value=0)
# 使用均值填充
>>> s2 = s.fillna(value=s.mean())
# 使用中位数填充
>>> s3 = s.fillna(value=s.median())
# 使用众数填充(众数可能有多个,此处取第一个)
>>> s4 = s.fillna(value=s.mode()[0])# 2. method参数:使用指定方法填充# ffill方法:用 NaN 前面的一个值填充
>>> s5 = s.fillna(method='ffill')
# bfill方法:用 NaN 后面的一个值填充
>>> s6 = s.fillna(method='bfill')

若要在原数据上修改,可以将fillna()中的inplace参数设置为True,此时该方法无返回值;同理,上述缺失值填充的方法也适用于 dataframe;

特征归一化

多维特征取值差异较大时会影响梯度下降的收敛效果,所以需要对每个特征进行归一化处理。如果不进行特征归一化处理,模型收敛会很慢甚至可能不收敛。 特征归一化在 sklearn 中的使用方法如下:

>>> import numpy as np
>>> from sklearn.preprocessing import MinMaxScaler
# 创建一个特征矩阵,每一行是一个样本的特征向量
>>> X = [[2,35,500],[6,75,700],[8,65,800],[18,85,900]]
# 特征归一化
>>> MinMaxScaler().fit_transform(X)
array([[0.   , 0.   , 0.   ],[0.25 , 0.8  , 0.5  ],[0.375, 0.6  , 0.75 ],[1.   , 1.   , 1.   ]])

假如有100个样本,特征 xxx 在这100个样本上的取值最大值为max(x)max(x)max(x),最小值为min(x)min(x)min(x),现在要把特征 xxx 的这100个取值映射到区间[a,b][a,b][a,b]上,则映射后的特征值可以使用如下公式进行计算:
x=x−min(x)max(x)−min(x)(b−a)+ax=\frac{x-min(x)}{max(x)-min(x)}(b-a)+ax=max(x)min(x)xmin(x)(ba)+a
sklearn.preprocessing中的MinMaxScaler在实例化时,有一个feature_range参数,默认为(0,1),可以通过下面的代码了解其实现细节:

# 各维特征归一化范围,默认(0,1)
>>> feature_range = (0, 1)
# 上文代码中的X转化为numpy数组,方便计算
>>> X = np.array(X)
# 各列数值减去所在列的最小值/各列取值长度,得到的是(0,1)内的特征值
>>> X_std = (X-X.min(axis=0))/(X.max(axis=0)-X.min(axis=0))
# 归一化区间内的最小值和最大值
>>> min, max = feature_range
# 得到特定范围内的特征值
>>> X_scaled = X_std * (max - min) + min
>>> X_scaled
array([[0.   , 0.   , 0.   ],[0.25 , 0.8  , 0.5  ],[0.375, 0.6  , 0.75 ],[1.   , 1.   , 1.   ]])

连续特征离散化

工业界很少将连续值作为逻辑回归模型的特征输入,而是将连续特征离散化为一系列的 0、1 特征交给逻辑回归模型。连续特征离散化(特征分箱)本质上是一个分段函数的映射过程,属于非线性变换。这种变换可以克服极端值和异常值对模型训练效果的影响,避免模型对噪声数据过拟合。一般常用几种简单的离散化方法:自定义分箱、等距分箱和等频分箱。
自定义分箱

>>> import pandas as pd
# bins 是分箱边界值
>>> a = pd.cut([1,3,4,5,8],bins=[0,3,5,7,9])
# 获取原数据的分箱索引,作为离散化的特征
>>> a.codes
array([0, 0, 1, 1, 3], dtype=int8)
# 查看分箱统计信息:类别、计数、频率
>>> a.describe()counts    freqs
categories
(0, 3]       2   0.4
(3, 5]       2       0.4
(5, 7]       0       0.0
(7, 9]       1       0.2

[1,3,4,5,8]中的每个特征值分别被映射到了(0,3]、(3,5]、(5,7]、(7,9]4个区间中,这些区间是通过bins这个参数人为定义好的

等距分箱

>>> b = pd.cut([4,10,20,10,6],bins=4,precision=0)
# 获取原数据的分箱索引,作为离散化的特征
>>> b.codes
array([0, 1, 3, 1, 0], dtype=int8)
# 查看分箱统计信息:类别、计数、频率
>>> b.describe()counts     freqs
categories
(4.0, 8.0]     2  0.4
(8.0, 12.0]    2  0.4
(12.0, 16.0]   0  0.0
(16.0, 20.0]   1  0.2

等距分箱和自定义分箱的主要区别在于bins参数,上述代码将[4,10,20,10,6]中的每个值映射到了长度相等的4个区间内;

等频分箱

>>> import pandas as pd
# q 表示分位数,q=4 表示每个分箱内的样本数占总样本的1/4
>>> c = pd.qcut([9,5,2,1,30,50,75,80],q=4,precision=0)
# 获取原数据的分箱索引,作为离散化的特征
>>> c.codes
array([1, 1, 0, 0, 2, 2, 3, 3], dtype=int8)
# 查看分箱统计信息:类别、计数、频率
>>> c.describe()counts    freqs
categories
(0.0, 4.0]     2 0.25
(4.0, 20.0]        2 0.25
(20.0, 56.0]   2 0.25
(56.0, 80.0]   2 0.25

可以看到:每个分箱内的样本数占比都是 0.25,即实现了等频分箱

离散特征one-hot编码

特征分箱之后,可以对离散型变量做进一步的OneHot编码处理,假设已有一个表示职业类型的特征 jobjobjob ,共有 4 个取值:医生、老师、学生、警察,将其 OneHot 编码之后,特征 jobjobjob 从1维升高至4维:

特征 jobjobjob 的每一个取值都可以用一个4维向量来表示,该向量的元素非零即一,形象把它叫做OneHot特征向量:向量中只有一个元素取值为 1,其余元素的取值均为 0;

使用numpy实现onehot编码:

import numpy as np# 定义onehot编码函数
def onehot(size,index):result = np.zeros(size,dtype=int)result[index] = 1return result# 特征取值数量
feature_size = 4# 依次遍历该特征每个取值的索引,输出onehot编码
for i in range(feature_size):print(onehot(feature_size,i))# 运行结果如下:
# [1 0 0 0]
# [0 1 0 0]
# [0 0 1 0]
# [0 0 0 1]

也可以使用 sklearn 封装的方法来实现这一过程,并且可以对多个类别特征同时进行OneHot 编码:

>>> from sklearn.preprocessing import  OneHotEncoder
# 样本特征矩阵,每一行为一个样本,每一列为一个特征
>>> X = [[0, 0, 0],[1, 1, 1],[0, 2, 2],[1, 0, 3]]
# sparse=True时,返回稀疏存储的矩阵,否则返回numpy数组
>>> OneHotEncoder(sparse=False,dtype=int).fit_transform(X)
array([[1, 0, 1, 0, 0, 1, 0, 0, 0],[0, 1, 0, 1, 0, 0, 1, 0, 0],[1, 0, 0, 0, 1, 0, 0, 1, 0],[0, 1, 1, 0, 0, 0, 0, 0, 1]], dtype=int64)

X有3列特征,第一列特征有2个取值,对应的OneHot特征向量维度为2;第二列特征有3个取值,对应的OneHot特征向量维度为3;第三列特征有4个取值,对应的OneHot特征向量维度为4;三列特征的OneHot特征向量拼接起来即为最后的样本特征向量(9维)

ID特征Embedding

在数据采集阶段,得到的大部分数据都是ID类的特征,比如用户ID、商品ID、店铺ID等,这些ID类的特征本质上是一些离散型的特征,并且取值数非常多,如果将所有的ID类特征进行OneHot 编码,得到的样本特征向量维度会非常高。

所以,后来出现了特征Embedding技术:将高维稀疏的特征向量映射到低维空间中,得到低维稠密的特征向量。如图所示是OneHot特征向量通过Embedding技术映射到低维向量空间的原理示意图:


Embedding矩阵可以通过两种方式得到:一种是预训练,另一种是作为模型参数先随机初始化,然后通过特定任务训练更新;

获取Embedding特征实现如下,实际就是矩阵乘法:

>>> import numpy as np
# OneHot特征向量维度,embedding特征向量维度
>>> feature_size,emb_size = 6,4
# 随机初始化的embedding矩阵
>>> w = np.random.randn(feature_size,emb_size,)
>>> w
array([[ 0.73541001, -0.55067429,  0.92116447, -0.19135519],[ 0.42867673,  0.9408499 , -0.30474845, -0.83732498],[-0.13808742,  1.3788042 ,  0.24563835,  0.38514269],[-0.95431867, -1.79045266,  0.92817987,  1.20328236],[-1.14640283,  1.25422919,  0.48205121,  0.33136124],[ 0.14712547,  0.2786036 ,  0.70921497,  2.69474609]])
# 通过矩阵乘法得到embedding特征向量
>>> np.array([0,0,0,0,0,1]).dot(w)
array([0.14712547, 0.2786036 , 0.70921497, 2.69474609])

特征构造方法

基础特征不足会限制模型的表达能力,所以就有必要人为构造一些交叉特征和统计特征来提升模型效果,假设有以下场景:数据集只有两种特征,性别(男,女)和职业(医生,老师,警察);

一种方法是直接将两列特征的文本字符串进行拼接,得到组合特征:

统计特征的加入对于模型预测效果的提升也是至关重要的,常见的统计特征比如有前1天用户的点击量、前1天商品的点击量、前1天用户的浏览量、前1天商品的曝光量、前1天用户的点击率、前1天商品的点击率,这些特征在时间窗口和行为类型上可以进一步拓展;

此外,还有前1天用户行为间隔时间的均值和方差、前1天商品行为间隔时间的均值和方差、用户过去一天的行为次数 / 用户过去三天的行为次数均值、商品过去一天的行为次数 / 商品过去三天的行为次数均值等,这些特征也可以在时间窗口和行为类型上做进一步拓展;总之,构造特征的方法灵活多变,根据不同问题使用适当的构造方法即可。

AUC指标补充

AUC是CTR预估中经常用到的离线评价指标,用来衡量模型将正样本(可以理解为点击率较高的样本)排在前面的能力。理论上AUC越高,带来的广告收入也会越高;

首先回顾第六课中AUC的内容,逻辑回归预测为正例的样本可以分为两类:一类是真正例(True Positive,简写为TP),一类是假正例(False Positive,简写为FP)。顾名思义,在这些预测为正例的样本中,TP是预测对了的,FP是预测错了的;

在真实正样本中统计TP的比例可以得到真正例率(True Positive Rate,简写为TPR),在真实负样本中统计FP的比例可以得到假正例率(False Positive Rate,简写为FPR);逻辑回归选择不同的分类阈值可以得到不同的TPR和FPR,由此得到一系列(FPR:x轴,TPR:y轴)数据点,根据这些数据点可以画出一条ROC曲线,ROC 曲线下的面积即:AUC(Area Under ROC Curve);

实际计算时,会以每个样本的预测值分别作为分类阈值,mmm 为样本个数,将每个样本的概率预测值作为阈值,可以得到一个数据点(FPR,TPR),mmm 个样本总共可以得到 mmm 个数据点,对应的 AUC 是由 m−1m-1m1 个梯形的面积累加得到的:
AUC=12∑i=1m−1(TPRi+1+TPRi)(FPRi+1−FPRi)AUC=\frac{1}{2}\sum_{i=1}^{m-1}(TPR_{i+1}+TPR_{i})(FPR_{i+1}-FPR_{i})AUC=21i=1m1(TPRi+1+TPRi)(FPRi+1FPRi)
实现AUC的函数如下:

def calc_auc(raw_arr):"""raw_arr: 二维数组,其中每个元素的定义为 [预测为正类的概率,样本标记(0或1)]return: auc"""# 按照正类概率从大到小对数组排序arr = sorted(raw_arr, key=lambda d:d[0], reverse=True)# 统计数组中正负样本的个数pos, neg = 0., 0.for record in arr:if record[1] == 1.:pos += 1else:neg += 1# 计算不同概率阈值下的fpr和tprfp, tp = 0., 0.xy_arr = []for record in arr:if record[1] == 1.:tp += 1else:fp += 1xy_arr.append([fp/neg, tp/pos])# 计算aucauc = 0.prev_x = 0.prev_y = 0.for x, y in xy_arr:if x != prev_x:# 曲边梯形的面积:(上底+下底)*高/2auc += ((prev_y + y)*(x - prev_x)/ 2.)prev_x = xprev_y = yreturn auc

实验:LR预估CTR

点击率(Click-Through-Rate,简称CTR)是互联网广告中经常提到一个概念,通过机器学习算法预估广告点击率,然后将预测值较高的广告展现给用户,如果用户点击了这些CTR预估较高的广告,就可以为平台带来巨大的广告收入;

LR曾是各大互联网公司在CTR预估上使用的主流模型。它有着可解释性强、易于并行化、便于在线学习等不可替代的优势。本次实验是基于LR的广告点击率预估;

实验使用的数据集是电商领域数据集,数据集在个人资源处,共计200万的样本记录。原始特征均为ID类特征,需要将所有ID类特征onehot编码,然后送入逻辑回归模型,预测用户对商品发生购买行为的概率,并使用AUC作为模型的评价指标。(购买属于电商网站里点击行为的一种,商品作为广告在网站里进行曝光)

首先读取数据:

import pandas as pd
data_path = 'data.zip'
data_col = ['userid','itemid','categoryid','action','timestamp']
df = pd.read_csv(data_path,names=data_col,compression='zip')
df.head()

字段说明:

  • userid 用户ID
  • itemid 商品ID
  • categoryid 商品类目ID
  • action 用户行为类型
  • timestamp 时间戳

输出每个字段空值数量:

df.isna().sum()
"""
userid        0
itemid        0
categoryid    0
action        0
timestamp     0
dtype: int64
"""

查看每个字段的取值有多少种:

feats=['userid','itemid','categoryid','action','timestamp']for f in feats:print(f,":",len(set(df[f].tolist())))"""
userid : 19544
itemid : 629096
categoryid : 6494
action : 4
timestamp : 627028
"""

可看出,用户行为可分为四类,因为action有4种取值;


补充nonzero()用于返回数组array中非零元素的索引;

比如有编码:

sample = [0]*7+[1]+[0]*8+[1]+[0]*5+[1]+[0]*3
sample
"""
[0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0]
"""

返回这个稀疏特征的非零索引为:

import numpy as np
np.array(sample).nonzero()
"""
(array([ 7, 16, 22], dtype=int64),)
"""

定义特征重编码函数,将样本矩阵中的每一列特征使用自然数统一编码,编码后的特征值相当于原特征的唯一索引号,每个特征的取值,编码后都不再重复,比如:

def recode(features):'''features: 二维numpy数组:一行为一个样本,一列为一个特征'''# 结果列表result = []# 将原特征统一编码的索引值index = 0# 遍历每一列特征for f in features.T:        f_key = set(f)f_value = range(index,index+len(f_key))f_dict = dict(zip(f_key,f_value))# 根据编码字典将原特征转换为自然数索引result.append([f_dict[key] for key in f])index += len(f_key)# 返回新的样本矩阵return np.array(result).Tarr=np.array([[0,0,0],[1,1,1],[2,2,2],[3,3,3]])arr_new=recode(arr)
"""
array([[ 0,  4,  8],[ 1,  5,  9],[ 2,  6, 10],[ 3,  7, 11]])
"""

定义函数计算交叉熵损失:

# 单个样本二分类的交叉熵损失
def cross_entropy_loss(y_prob,y):'''y_prob:正类预测概率,取值0-1y:样本标记值,正样本为1,负样本为0,y为向量'''return -1*(y*np.log(y_prob)+(1-y)*np.log(1-y_prob))

定义模型,本实验的逻辑回归不同于第七课的逻辑回归,本实验的输入特征准确来说,原数据的每个特征的每种取值才是一个特征(这样设计是因为输入数据比如userid的取值不是一种连续的曲线,id数据代表不同的类别),因此为:

class LR: def __init__(self,feat_size,alpha=0.2):'''feat_size:高维稀疏特征向量的维度'''        # LR的特征权重向量self.w = np.random.randn(feat_size)# 学习率self.alpha = alpha# 模型预测def predict_prob(self,x):'''x:样本输入特征向量中的非零元素索引值'''# 线性回归的输出值z =  np.dot(x, self.w[x])# 逻辑回归的正类概率输出值return 1/(1+np.exp(-z))# 模型训练(随机梯度下降法)def train(self,X_train,y_train):# 遍历训练集的每个样本for feat,label in zip(X_train,y_train):y_prob = self.predict_prob(feat)# 遍历样本非零特征索引for i in feat:# 更新对应的权重参数self.w[i] += -1*self.alpha*(y_prob-label)*i# 模型评估(loss和auc)def metric(self,X_test,y_test):# 存放每个测试样本的交叉熵损失cost = []# 存放每个测试样本的预测概率和真实标记值preds = []# 遍历测试集样本for feat,label in zip(X_test,y_test):# 计算当前样本的正类概率预测值y_prob = self.predict_prob(feat)# 计算当前样本的交叉熵损失loss = cross_entropy_loss(y_prob,label)preds.append([y_prob,label])cost.append(loss)# 打印测试集的平均损失print('test_cost:',np.mean(cost))# 打印模型在测试集上的AUCprint('test_auc:',calc_auc(preds))from sklearn.model_selection import train_test_splitif __name__ == '__main__':# 取特征矩阵:200万行x3列的numpy数组X = recode(df.iloc[:,:3].values)# 取样本标记:包含200万元素的一维数组 ,根据 action 字段获取样本标记,若action=buy,则label=1,否则label=0y =np.array([1 if action=='buy' else 0 for action in df['action'].values])# 划分训练集和测试集,测试集占20%X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2)# 查看有多少个特征feat_size = X.max()+1print('特征总数:',feat_size)# 特征总数: 655134# 模型实例化model = LR(feat_size)# 模型训练迭代,打印评价指标for i in range(10):print('\nepoch =',i)model.train(X_train,y_train)model.metric(X_test,y_test)

第八课.特征工程与CTR预测相关推荐

  1. 特征工程与CTR预估

    特征工程与CTR预估 缺失值如何处理 缺失值判断 缺失值删除 缺失值填充 连续特征归一化 连续特征离散化 自定义分箱 等距分箱 等频分箱 离散特征OneHot编码 ID特征Embedding 特征构造 ...

  2. ML之DataScience:基于机器学习处理数据科学(DataScience)任务(数据分析、特征工程、科学预测等)的简介、流程、案例应用执行详细攻略

    ML之DataScience:基于机器学习处理数据科学(DataScience)任务(数据分析.特征工程.科学预测等)的简介.流程.案例应用执行详细攻略 目录 数据科学的任务(数据分析.特征工程.科学 ...

  3. Keras之MLP:利用MLP【Input(8)→(12)(relu)→O(sigmoid+二元交叉)】模型实现预测新数据(利用糖尿病数据集的八个特征实现二分类预测

    Keras之MLP:利用MLP[Input(8)→(12)(relu)→O(sigmoid+二元交叉)]模型实现预测新数据(利用糖尿病数据集的八个特征实现二分类预测 目录 输出结果 实现代码 输出结果 ...

  4. Keras之DNN:利用DNN【Input(8)→(12+8)(relu)→O(sigmoid)】模型实现预测新数据(利用糖尿病数据集的八个特征进行二分类预测

    Keras之DNN:利用DNN[Input(8)→(12+8)(relu)→O(sigmoid)]模型实现预测新数据(利用糖尿病数据集的八个特征进行二分类预测 目录 输出结果 设计思路 实现代码 输出 ...

  5. 特征工程——二手车交易预测

    一. 特征工程的重要性和处理 1. 特征工程一般包括特征使用.特征获取.特征处理.特征选择和特征监控. 业界广泛流传着这样的一句话:"数据和特征决定了机器学习的上限,而模型和算法只是逼近这个 ...

  6. 机器学习特征工程之特征抽取

    1.数据集 数据集是特征抽取的源数据.常用数据集的结构组成:特征值+目标值. 可用数据集: kaggle网址:http://www.kaggle.com/datasets UCI数据集:http:// ...

  7. 【直播】王茂霖:二手车交易价格预测-千变万化特征工程(河北高校数据挖掘邀请赛)

    二手车交易价格预测-千变万化特征工程 目前 河北高校数据挖掘邀请赛 正在如火如荼的进行中.为了大家更好的参赛,王茂霖分享了 从0梳理1场数据挖掘赛事!,完整梳理了从环境准备.数据读取.数据分析.特征工 ...

  8. 电信用户流失预测案例(2)(特征工程)

    [Kaggle]Telco Customer Churn 电信用户流失预测案例 第二部分导读   在上一部分中,我们已经完成了对数据集背景解读.数据预处理与探索性分析.在数据背景解读中,我们介绍了数据 ...

  9. ML之FE:利用FE特征工程(单个特征及其与标签关系的可视化)对RentListingInquries(Kaggle竞赛)数据集实现房屋感兴趣程度的多分类预测

    ML之FE:利用FE特征工程(单个特征及其与标签关系的可视化)对RentListingInquries(Kaggle竞赛)数据集实现房屋感兴趣程度的多分类预测 目录 输出结果 设计思路 核心代码 输出 ...

最新文章

  1. 使用css实现瀑布流的效果
  2. 吴恩达老师,被曝靠「教书」实现首个IPO上市,估值50亿美元
  3. 第八周实践项目9 算法库——广义表
  4. Java可扩展实体_java – 是否可以通过扩展一个POJO来构建一个JPA实体?
  5. 【转】PowerShell入门(五):Cmd命令与PowerShell命令的交互
  6. linux字符设备开发
  7. 如何摆脱「自我否定」状态
  8. 【吐槽】VS2012的安装项目只能用InstallShield Limited Edition
  9. nginx 中location和root,你确定真的明白他们关系?
  10. shell脚本文件中ll提示找不到命令
  11. CAD批量提取数值lisp插件_CAD批量获取文本坐标及内容
  12. DeepLearning[花书] 参考资料
  13. turtle---见证小海龟的浪漫之画
  14. iOS上应用如何兼容32位系统和64位系统
  15. linux桌面 输入法 原理,安装ubuntu 7.10桌面版后无中文输入法的解决
  16. 用vue实现动态组织结构图
  17. python中time库的时间单位是秒而非毫秒
  18. A型钽电容和B型钽电容的区别?
  19. mysql intersect 使用方法_MySQL INTERSECT运算符
  20. 【极简版GH60】【GH60剖析】【四】轴的安装与焊接

热门文章

  1. 全局负载均衡与CDN内容分发
  2. 最牛逼的核心框架,没有之一!
  3. 阿里飞猪搜索技术的应用与创新
  4. 出现这四种情况,才是考虑分库分表的时候!
  5. 实践:使用Spring 原生注解来快速实现 策略模式 + 工厂模式
  6. Java面试官:给Java面试者的八点建议
  7. 飞书面向所有企业和组织免费开放,2020我们一起拥抱线上协作新方式
  8. c# 使用dotnetbar 控件绘制曲线图形
  9. android设置tls版本,Android O移除HttpsURLConnection中不安全的TLS版本回退
  10. android manifest 分辨率,android程序界面自动适应屏幕分辨率例子