甲乙小朋友的房子

甲乙小朋友很笨,但甲乙小朋友不会放弃

0%

用pandas求差集

从df1中去除掉df2的内容

data={'id':[1,2,3]} data2={'id':[3,1,4]} df1=pd.DataFrame(data) df2=pd.DataFrame(data2) print df1 print df2 df3=df1[~df1['id'].isin(df2['id'])] print df3

求某个时间区间的数据集

tart_t='2016-04-05 00:00:00' end_t='2016-04-16 00:00:00' action.time=pd.to_datetime(action.time,format='%Y-%m-%d %H:%M:%S') starttime=pd.to_datetime(start_t,format='%Y-%m-%d %H:%M:%S') endtime=pd.to_datetime(end_t,format='%Y-%m-%d %H:%M:%S') action=action[(action.time<endtime)&(action.time>=starttime)]

dummies

我理解get_dummies是将拥有不同值的变量转换为0/1数值。打个比方,小明有黄、红、蓝三种颜色的帽子,小明今天戴黄色帽子用1表示,红色帽子用2表示,蓝色帽子用3表示。但1、2、3数值大小本身是没有意义的,只是用于区分帽子的颜色,因此在实际分析时,需要将1、2、3转化为0、1,如下代码所示:

import pandas as pd
xiaoming=pd.DataFrame([1,2,3],index=['yellow','red','blue'],columns=['hat'])
print(xiaoming)
hat_ranks=pd.get_dummies(xiaoming['hat'],prefix='hat')
print(hat_ranks.head())

            hat
yellow    1
red       2
blue      3
        hat_1  hat_2  hat_3
yellow      1      0      0
red         0      1      0
blue        0      0      1

其它to DataFrame

dict to DataFrame

pd.DataFrame(d.items())

fillna

用同组的均值填补

>>> df
  name  value
0    A      1
1    A    NaN
2    B    NaN
3    B      2
4    B      3
5    B      1
6    C      3
7    C    NaN
8    C      3
>>> df["value"] = df.groupby("name").transform(lambda x: x.fillna(x.mean()))
>>> df
  name  value
0    A      1
1    A      1
2    B      2
3    B      2
4    B      3
5    B      1
6    C      3
7    C      3
8    C      3

ipython运行python

启动新的namespace运行: %run main.py

在原有的namespace运行:%run -i main.py

Object特性

  • 所有类的超类
  • 可以引用任何对象

equals方法

Object类中的equals方法判断两对象是否有相同的引用

equals的重写

在实际coding中,一般我们都会对equals方法进行重写,例:

  1. 对于超类Employee

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    class Employee{
    ...
    public boolean equals(Object otherObject){
    //如果otherObject与this是同一个引用
    if(this == therObject) return true;

    //如果therObject为空
    if(therObject == null) return false;

    //如果类型不同
    if(getClass() != otherObject.getClass()) return false;

    //现在我们已经确认了otherObject是一个非空的Employee对象
    //为了比较实例域,我们将otherObject转换为Employee类型
    Employee other = (Employee) otherObject

    //检测实例域是否相等
    return name.equals(other.name)
    && salary == other.salary
    && hireDay.equals(other.hireDay);
    }
    }

    为了防止this.namethis.hireDay可能为null的情况,将最后一句改为:

    1
    2
    3
    return Obejcts.equals(name,other.name)
    && salary == other.salary
    && Objects.equals(hireDay,other.hireDay);

    Obejcts.equals(a,b)运行过程

    • 如果ab都为null,则返回true
    • 如果其中一个为null,返回false
    • 如果都不为null,则调用a.equals(b)
  2. 对于Employee的子类Manager

在子类中定义euqals方法时,要首先调用超类的euqals,然后比较子类中的实例域。

1
2
3
4
5
6
7
8
9
class Manager extends Employee{
...
public boolean equals(Object otherObject){
//先调用超类的equals方法,检验this与otherObject是否属于同一class
if(!super.equals(otherObject)) return false;
Manager other = (Manager) otherObejct;
return bonus == other.bonus
}
}

equals方法的特性

  • 自反性:若x!=null,则x.equals(x)应返回true
  • 对称性:y.equals(x)x.euqals(y)应返回同样结果
  • 传递性:若x.equals(y)返回true,且y.equals(z)返回true,则x.equals(z)也必须返回true
  • 一致性:若x与y的引用对象没有发生变化,则反复调用x.equals(y)应返回同样结果
  • 对任意非空引用x,则x.equals(null)应该返回false

相等测试

问题 如果隐式(this)和显式(传入的)的参数不属于同一类,equals方法如何处理?

解决方法一 在上例中,我们用到了getClass()

  //如果类型不同
        if(getClass() != otherObject.getClass()) return false;

解决方法二 有的程序员喜欢用instanceof检测: (instanceof 运算符是用来在运行时指出对象是否是特定类的一个实例)

if(!(otherObeject instanceof Employee)) return false;

方法一存在的问题e是一个Employee对象,m是一个Manager对象,且两者具有相同姓名、薪水和雇用日期。

如果Manager没有重新实现equals方法,那么当m1.equals(m2)比较时,就会使用到e.getClass() != m2.getClass()来进行比较,显然结果返回false,因此这不是正确的比较。

方法二存在的问题 那么对于e.equals(m)来说,instanceof返回true.(ManagerEmployee的一个实例) 但对于m.equals(e)来说,instanceof返回false

!!!!!违反了对称性!

总结

  • 如果子类拥有自己的相等概念,例如若两个Manager对象的姓名、薪水和雇用日期(父类Employee的域)均相等,而奖金(子类Manager的域)不相等,就认为两个Manager不相等。此时可以用getClass检测
  • 如果使用雇员ID(父类Employee的域)来作为相等检测标准,并这个标准适合所有的子类,就可以用instanceof检测,并将Employeeequals申明为final

完美的equals

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public boolean equals(Object otherObject){
//检测this与otherObject是否引用同一对象
if(this==otherObject)return true;
//检测otherObject是否为null
if(otherObject == null) return false;
//比较this与Object是否属于同一个类,有以下两种方案:
//如果equals的语义在每个子类中都有改变,就用getClass:
if(getClass() != otherObject.getClass()) return false;
//如果所有子类都有统一equals语义,则用instanceof检测:
if(!(otherObject instanceof ClassName)) return false;
//将otherObject转换为相应的类类型
ClassName other = (ClassName) otherObject
//对所需要的域进行比较
return field1 == other.field1
&& field2 == other.field2
&& Objects.equals(field3,other field3)
&& ...;
}

注意:如果在子类中重新定义equals,则要在其中包含调用super.equals(other)

hashCode方法

hashCode : 散列码

使用:

1
2
3
4
int hash = 0
for ( int i = 0 ; i < lenth() ; i++ ){
hash = 31 * hash + charAt(i)
}

注意

  • 每个对象都有一个默认的散列码,值为对象的存储地址
  • 字符串的散列码是由内容导出的,因此st有相同hashcode
  • 如果重新定义了equals方法,就必须重新定义hashCode方法,且通过equal测试的两个对象的hashCode应该也是相等的,以便用户可以将对象插入到散列表中
  • hashCode方法应该返回一个整型数值,并使得不同对象的hashCode更均匀。

例如:

1
2
3
4
5
6
7
class Employee{
public int hashCode(){
return 7 * name.hashCode()
+ 11 * new Double(salary).hashCode()
+ 13 * hireDay.hashCode();
}
}

更方便的方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
    public int hashCode(){
return Objects.hash(name,salary,hireDay);
}```


# toString方法

用途:返回表示对象值的字符串

例如:

```Java
public String toString(){
return getClass().getName()
+ "[name=" + name
+ ",salary=" + salary
+ ",hireDay=" + hireDay
+"]";
}

特别地:

  1. 只要对象与一个字符串通过操作符“+”连接起来,或使用System.out.println(x),Java编译就会自动调用toString方法。
  2. Object类定义了toString方法,用来打印输出对象所属的类名和散列码。
  3. 有趣的是,数组继承了object类的toString方法。修正的方法是采用Array.toString.例如String s = Array.toString(luckyNumbers)。多维数组需要调用Array.deepToString方法。

本文大多来自于参考文献2.的翻译。

Xgboost参数

XGBoost作者将参数分为3类:

  1. 基本(General)参数:指导整体函数
  2. 提升(Booster)参数:在每一步指导每个提升(树或回归)
  3. 学习任务(Learning Task Parameters)参数:指导优化模型表现

基本参数

booster

  1. 默认值:gbtree
  2. 含义:在每次迭代中的模型类型,有两种选择:
  • gbtree:树模型
  • gblinear:线性模型

silent

  1. 默认值:0
  2. 含义
  • 0=运行时打印running messages
  • 1=不打running messages

nthread

  1. 运行时的最大线程数
  2. 默认值:占当前计算机的最大线程

提升参数

这里仅仅介绍树模型(gbtree)的相关参数。

eta

  1. 学习步长
  2. default=0.3
  3. 一般调整下限为0.01-0.2

min_child_weight

  1. 表示所有孩子节点(all observations required in a child)的最小权重和
  2. 一般都用来控制过拟合。
  3. 但如果调得太高就会导致欠拟合。
  4. default=1

max_depth

  1. default=6
  2. 用来控制过拟合
  3. 一般在3-10之间

gamma

  1. default=0
  2. range: [0,∞]
  3. 模型在默认情况下,对于一个节点的划分只有在其损失函数得到结果大于0的情况下才进行,而gamma给定了所需的最低损失函数的值
  4. gamma值使得算法更conservation,且其值依赖于损失函数,在模型中应该进行调参。

max_delta_step

  1. default=0
  2. 在最大化步长的时候,我们允许每个树的权重去估算它。
  3. 当max_delta_step=0时,意味着没有误差
  4. 当max_delta_step>0时,意味着结果更保守
  5. 一般不需要调参。但当类别及不平衡的逻辑回归时可能会用到。

subsample [default=1]

Same as the subsample of GBM. Denotes the fraction of observations to be randomly samples for each tree. Lower values make the algorithm more conservative and prevents overfitting but too small values might lead to under-fitting. Typical values: 0.5-1

colsample_bytree [default=1]

Similar to max_features in GBM. Denotes the fraction of columns to be randomly samples for each tree.

Typical values: 0.5-1 ### colsample_bylevel [default=1] Denotes the subsample ratio of columns for each split, in each level. I don’t use this often because subsample and colsample_bytree will do the job for you. but you can explore further if you feel so.

lambda [default=1]

L2 regularization term on weights (analogous to Ridge regression) This used to handle the regularization part of XGBoost. Though many data scientists don’t use it often, it should be explored to reduce overfitting.

alpha [default=0]

L1 regularization term on weight (analogous to Lasso regression) Can be used in case of very high dimensionality so that the algorithm runs faster when implemented

scale_pos_weight [default=1]

A value greater than 0 should be used in case of high class imbalance as it helps in faster convergence. Control the balance of positive and negative weights, useful for unbalanced classes. A typical value to consider: sum(negative cases) / sum(positive cases) See Parameters Tuning for more discussion. Also see Higgs Kaggle competition demo for examples: R, py1, py2, py3

控制正负样本权重的平衡。一般对非平衡的很有用。一个很特殊的取值是:负样本个数/正样本个数。

不均衡数据在xgboost中的处理

官方文档:

对于一些case,比如:广告点击日志,数据集极不平衡。这会影响xgboost模型的训练,有两个方法来改进它。

如果你关心的预测的ranking order(AUC): – 通过scale_pos_weight来平衡正负类的权重 – 使用AUC进行评估

如果你关心的是预测的正确率: – 不能再平衡(re-balance)数据集 – 将参数max_delta_step设置到一个有限的数(比如:1)可以获得效果提升.

学习任务参数

objective [default=reg:linear]

  1. default=reg:linear
  2. 定义学习任务及相应的学习目标,可选的目标函数如下:
  • “reg:linear” –线性回归。
  • “reg:logistic” –逻辑回归。
  • “binary:logistic” –二分类的逻辑回归问题,输出为概率。
  • “binary:logitraw” –二分类的逻辑回归问题,输出的结果为wTx。
  • “count:poisson” –计数问题的poisson回归,输出结果为poisson分布。在poisson回归中,max_delta_step的缺省值为0.7。(used to safeguard optimization)
  • “multi:softmax” –让XGBoost采用softmax目标函数处理多分类问题,同时需要设置参数num_class(类别个数)
  • “multi:softprob” –和softmax一样,但是输出的是ndata * nclass的向量,可以将该向量reshape成ndata行nclass列的矩阵。没行数据表示样本所属于每个类别的概率。
  • “rank:pairwise” –set XGBoost to do ranking task by minimizing the pairwise loss

eval_metric

  1. 默认值根据objective参数调整
  2. 用来验证数据的参数
  3. 几个典型值:
  • rmse – root mean square error
  • mae – mean absolute error
  • logloss – negative log-likelihood
  • error – Binary classification error rate (0.5 threshold)
  • merror – Multiclass classification error rate
  • mlogloss – Multiclass logloss
  • auc: Area under the curve

seed [default=0]

The random number seed. Can be used for generating reproducible results and also for parameter tuning.

例:CTR问题的正样本过少

参考文献[xgboost导读和实战]

《xgboost导读和实战》中提到,CTR问题的正样本很稀疏,根据理论推导,会导致叶子节点权重变大,进而导致每一步的估计step过大。

这时可以调节以下参数:

min_child_weight

默认为1.是每个叶子里h的和至少是多少。对正负样本不均衡时的0-1分类而言,假设h在0.01附近,min_child_weight为1意味着叶子节点中最少需要包含100 个样本。这个参数非常影响结果,控制叶子节点中二阶导的和的最小值,该参数值越小,越容易 overfitting。

eta shrinkage 参数,用于更新叶子节点权重时,乘以该系数,避免步长过大。参数值越大,越可能无法收敛。把学习率eta设置的小一些,小学习率可以使得后面的学习更加仔细。

scale_pos_weight 如果优化的是仅仅展示排序,就是AUC的话,可以采用平衡正负样本权重的 办法调大正样本权重。设置 scale_pos_weight就可以把正样本权重乘这个系数。如果还需要优化回归的性能,还需要在此基础上做下 recalibration。

max_delta_step 如果设立了该值,对叶子节点的权重值做了约束在 [max_delta_step,max_delta_step]。以防在某些 loss 下权重值过大,默认是 0(其实代表 inf)。可以试试把这个参数设置到 1-10 之间的一个值。这样会防止做太大的更新步子, 使得更新更加平缓。

交叉验证

xgboost自带的交叉验证据说很好用。

下面是参考文献3给出的一个使用案例:

cvresult = xgb.cv(xgb_param, 
                    xgtrain, num_boost_round=alg.get_params()['n_estimators'],
                    nfold=cv_folds,
                    metrics='auc', early_stopping_rounds=early_stopping_rounds, show_progress=False)

其中: - xgb_params:参数 - xgtrain:训练集 - num_boost_round:树个数(迭代次数) - nfold:kfold的k - metrics:在CV中的评价度量指标 - early_stopping_rounds:Activates early stopping. CV error needs to decrease at least every round(s) to continue. Last entry in evaluation history is the one from best iteration.

参考文献

  1. 《xgboost导读和实战》
  2. Xgboost深入浅出
  3. Complete Guide to Parameter Tuning in XGBoost (with codes in Python)
  4. xgboost参数官方文档
  5. XGBoost参数调优完全指南(附Python代码)
  6. 如何处理偏斜类(imbalanced classes)
  7. XGBoost Parameters

特征工程

别人的例子

特征选择

特征选择,就是从多个特征中,挑选出一些对结果预测最有用的特征。因为原始的特征中可能会有冗余和噪声。 特征选择和降维有什么区别呢?前者只踢掉原本特征里和结果预测关系不大的, 后者做特征的计算组合构成新特征。

过滤型

  • 方法:  评估单个特征和结果值之间的相关程度, 排序留下Top相关的特征部分。
  • 评价方式:Pearson相关系数, 互信息, 距离相关度。
  • 缺点:只评估了单个特征对结果的影响,没有考虑到特征之间的关联作用, 可能把有用的关联特征误踢掉。因此工业界使用比较少。
  • python包:SelectKBest指定过滤个数、SelectPercentile指定过滤百分比。

包裹型

  • 方法:把特征选择看做一个特征子集搜索问题, 筛选各种特 征子集, 用模型评估效果。
  • 典型算法:“递归特征删除算法”。
  • 应用在逻辑回归的过程:用全量特征跑一个模型;根据线性模型的系数(体现相关性),删掉5-10%的弱特征,观察准确率/auc的变化;逐步进行, 直至准确率/auc出现大的下滑停止。
  • python包:RFE

嵌入型

  • 方法:根据模型来分析特征的重要性,最常见的方式为用正则化方式来做特征选择。
  • 举例:最早在电商用LR做CTR预估, 在3-5亿维的系数特征上用L1正则化的LR模型。上一篇介绍了L1正则化有截断作用,剩余2-3千万的feature, 意味着其他的feature重要度不够。
  • python包:feature_selection.SelectFromModel选出权重不为0的特征。

参考文献

  1. 四月机器学习算法班—特征工程笔记

IO

linux/windows互传

需要安装:yum install -y lrzsz

从Windows上传文件[root@localhost src]# rz 从Linux下载文件sz nginx-1.6.2.tar.gz

linux互传

  1. 从服务器下载文件 scp username@servername:/path/filename /tmp/local_destination 例如scp codinglog@192.168.0.101:/home/kimi/test.txt 把192.168.0.101上的/home/kimi/test.txt的文件下载到 /tmp/local_destination
  2. 上传本地文件到服务器 scp /path/local_filename username@servername:/path
    例如scp /var/www/test.php codinglog@192.168.0.101:/var/www/ 把本机/var/www/目录下的test.php文件上传到192.168.0.101这台服务器上的/var/www/目录中
  3. 从服务器下载整个目录 scp -r username@servername:remote_dir/ /tmp/local_dir 例如:scp -r codinglog@192.168.0.101 /home/kimi/test /tmp/local_dir
  4. 上传目录到服务器 scp -r /tmp/local_dir username@servername:remote_dir 例如:scp -r test codinglog@192.168.0.101:/var/www/把当前目录下的test目录上传到服务器的/var/www/目录

文件夹

新建文件夹mkdir name

杂絮

查看硬盘情况df -h 合并文件cat *.csv > full.csv 查看文件$ ps -ef | grep 关键字

解压

批量解压.zip

两种方式:(经测试第一种好用)

ls *.zip | xargs -n1 unzip
unzip "*.zip"

批量解压.tar.gz

ls *.tar.gz | xargs -n1 tar xzvf    

vim

i——进入插入模式 ESC+:wq!:保存,退出 ESC+:q!:不保存,退出

VNC-tiger

安装: CentOs 7安装配置VNC Server digitalOcean,How to install vncserver in centos 7

使用:

help:vncserver -help 开启:vncserver :1 按照一定比例输出(屏幕旋转):vncserver 900x1440 :1 停止:vncserver -kill: 1 临时文档:/tmp/.X11-unix.

用sublime搭建简单的python

安装 Python,安装时选择添加路径到系统中,或者稍后自己添加也可 随便写个 demo,Ctrl + B 就可以运行了

sublime官方文档 各路方法

中文无法输入问题解决解决Ubuntu下Sublime Text 3无法输入中文 装python包

nohup

nohup command > nohup.out &

nohup相关: 将nohup后面的command设置成后台运行,并且将标准输出的日志重定向至文件nohup.out。 查看运行状态:jobs 查看日志内容:tail -f nohup.out (ctrl + c 退出查看) 查看进程信息: ps -ef |grep java ps -ef | grep rm 停止运行 kill %n 很好的参考文献:http://blog.sina.com.cn/s/blog_90546d6f0101en9y.html

更改某个目录的权限

chown -R -v ubuntu:ubuntu info

最近学校出了个科学上网的小网站,但流量有限,可以通过每天打卡来获取几十兆流量。奈何自己记性很差,总忘记打卡。因此决定写一个自动打卡的小程序。

思路总结

  1. 用抓包工具仔细分析下登陆以及回帖时post了哪些数据,这些数据从何而来(chrome)
  2. python requests库,用requests.Session().post来登陆和回帖,用get来读取页面内容;
  3. 登陆之后,正则找到“签到”或"不能签到",来进行下一步
  4. 若能签到,就发出签到URL的请求(因为链接从原来上来说是一种URL)
  5. 记录每次签到的log
  6. 最后的最后,使用写个.sh脚本,里面运行这个python程序,配置个相应的plist,每天自动执行(MAC OS)

模拟登陆原理

浏览器访问服务器的过程

一次完整的HTTP请求过程从TCP三次握手建立连接成功后开始。

  1. 用户发起请求(点击等)
  2. 浏览器向WEB服务器发出一个请求Http Request(请求)
  3. Web服务器发相应Http Response
  4. 浏览器解析

HTTP消息

更多关于HTTP请求过程,见参考资料一次完整的HTTP请求过程

HTTP 是一种无状态的协议,协议本身不保留之前的一切请求信息和响应信息,也就是说,对于一个刚刚发送了 HTTP 请求的客户端再次发起请求时,服务端并不知道之前访问过。这样设计的理由是为了更快地处理大量事务,确保协议的可伸缩性,而特意把 HTTP 协议设计如此简单。 但是,无状态导致业务处理就变得棘手了,一个简单的例子就是网上购物的时候,当我在一个页面把商品加入购物车的时候,正当我要跳转到支付页面时,如果没有一种机制来保持我的登录状态,那么之前选的商品全部丢失了。 因此,为了在无状态的HTTP协议之上维护会话状态,使服务器可以知道当前是和哪个客户端打交道,于是Cookie技术应运而生,Cookie通俗的理解就是服务端分配给客户端的一个标识。

Cookie原理

Cookie原理其实也非常简单,总共4个步骤维护HTTP会话。 1. 浏览器第一次发起HTTP请求时,没有携带任何Cookie信息,服务器收到请求并返回给浏览器的HTTP响应,同时HTTP响应包括了一个响应头Set-Cookie字段,它的值是要设置的Cookie。 2. 浏览器收到来自服务器的HTTP响应,响应头中发现有Set-Cookie字段,就会将该字段的值保存在内存或者硬盘中。 3. 浏览器下次给该服务器发送HTTP请求时,会将Cookie信息附加在HTTP请求的头字段Cookie中。 4. 服务器收到这个HTTP请求,发现请求头中有Cookie字段,便知道之前就和这个用户打过交道了。

理解了Cookie的基本原理之后,我们就可以尝试用Python来实现模拟登录。

点击登陆后,究竟发生了什么?

为了看看浏览器究竟做了什么,我们首先进入登录界面,随便输入一个密码,点击登录。打开Chrome开发者工具条(F12)。选择Network下的Headers:

从上图我们可以发现以下关键信息: 1. 登陆的URL地址是General下面的Request URL的那个。 2. 登录需要提供的表单(Form Data)有三个,email(用户名),passwd(密码),remember_me(就是那个“记住我”的选项,我们可以从它的html中搜索得出,如下图)。

到这里,基本上摸清了浏览器登录时所需要的数据是如何获取的了,那么现在就可以开始撸代码用Python模拟浏览器来登录。

由于我今天登陆的网站实在是太简易了,也不需要分析header就可以登陆。所以此处省略header的分析。

我打算基于python的requests库来完成这一过程。

python实现模拟登陆

1
2
3
4
5
6
7
8
9
10
11
email='jiayi797@163.com'
password='这里加密了'
loginurl = 'https://ssr.0v0.loan/auth/login'
# 这行代码,是用来维持cookie的,你后续的操作都不用担心cookie,他会自动带上相应的cookie
s = requests.Session()
# 我们需要带表单的参数
loginparams={'email':email,'passwd':password,'remember_me':'ture'}
# post 数据实现登录
r = s.post(loginurl,data=loginparams)
# 验证是否登陆成功,抓取首页看看内容
r = s.get(loginurl)

运行完这些后,我们发现r已经有内容了。接下来我们进行自动签到。

签到

我们查看r._content的内容,是主页的html内容。很长。为了方便分析,我只拿出“签到”标签下的内容:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<div class="col-md-6">
<div class="box box-primary">
<div class="box-header">
<i class="fa fa-pencil"></i>
<h3 class="box-title">签到获取流量</h3>
</div>
<!-- /.box-header -->
<div class="box-body">
<p> 每24小时可以签到一次。</p>
<p>上次签到时间:<code>2017-04-04 17:11:22</code></p>
<p id="checkin-btn">
<button id="checkin" class="btn btn-success btn-flat">签到</button>
</p>
<p id="checkin-msg"></p>
</div>
<!-- /.box-body -->
</div>
<!-- /.box -->
</div>
<!-- /.col (right) -->

需要注意的还有以下这个脚本:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<script>
$(document).ready(function () {
$("#checkin").click(function () {
$.ajax({
type: "POST",
url: "/user/checkin",
dataType: "json",
success: function (data) {
$("#checkin-msg").html(data.msg);
$("#checkin-btn").hide();
},
error: function (jqXHR) {
alert("发生错误:" + jqXHR.status);
}
})
})
})
</script>

从脚本中我们读到,签到的类型是post,url是/user/checkin, 因此我们试一下post一个这样的url会有什么后果。

1
2
3
checkinUrl="https://ssr.0v0.loan/user/checkin"
r=s.post(checkinUrl)#执行签到
r = s.get(loginurl)#查看签到结果

我们发现,签到栏的HTML已经变成了;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<div class="col-md-6">
<div class="box box-primary">
<div class="box-header">
<i class="fa fa-pencil"></i>
<h3 class="box-title">签到获取流量</h3>
</div>
<!-- /.box-header -->
<div class="box-body">
<p> 每24小时可以签到一次。</p>
<p>上次签到时间:<code>2017-04-05 14:08:02</code></p>
<p><a class="btn btn-success btn-flat disabled" href="#">不能签到</a></p>
<p id="checkin-msg"></p>
</div>
<!-- /.box-body -->
</div>
<!-- /.box -->
</div>
<!-- /.col (right) -->

即:

<button id="checkin" class="btn btn-success  btn-flat">签到</button>

变成了:

<a class="btn btn-success btn-flat disabled" href="#">不能签到</a>

大题框架搭完了。接下来要做的就是一些细节。

判断是否已签到

接下来要做的就是用正则,找到“签到”或者“不能签到”的对应标签,来获得一个当前状态。

首先我想到的是最简单的方式,用python自带的re.search()

1
2
3
4
5
6
7
8
9
10
11
12
def check(str):
hasCheckIn='<button id="checkin" class="btn btn-success btn-flat">'
noChecked='<a class="btn btn-success btn-flat disabled" href="#">'
yes=re.search(hasCheckIn,str)
if yes==None:
no=re.search(noChecked,str)
if no==None:
return -1 #什么都没找到
else:
return 0#找到了“不能签到”
else:
return 1#找到了“签到”

获取当前流量

先找到流量的对应标签:

1
2
3
4
5
6
7
8
<dl class="dl-horizontal">
<dt>总流量</dt>
<dd>1.53GB</dd>
<dt>已用流量</dt>
<dd>253.36MB</dd>
<dt>剩余流量</dt>
<dd>1.29GB</dd>
</dl>

想要获取标签内的内容(而不是暴力地匹配字符串),我们就需要用到另一种匹配方式——正则表达式

举个例子,我们想要获取之间的内容,那么我们可以这么写:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#正则表达式获取<tr></tr>之间内容  
res_tr = r'<tr>(.*?)</tr>'
m_tr = re.findall(res_tr,language,re.S|re.M)
for line in m_tr:
print line
#获取表格第一列th 属性
res_th = r'<th>(.*?)</th>'
m_th = re.findall(res_th,line,re.S|re.M)
for mm in m_th:
print unicode(mm,'utf-8'), #unicode防止乱
#获取表格第二列td 属性值
res_td = r'<td>(.*?)</td>'
m_td = re.findall(res_td,line,re.S|re.M)
for nn in m_td:
print unicode(nn,'utf-8')

其中,res是匹配模式。 r’是匹配模式的开头, <tr是先匹配字符串<tr .是匹配任意字符,除了换行符 *是匹配0个或多个的表达式 ?是匹配0个或1个由前面的正则表达式定义的片段,非贪婪方式 依次类推

re.S使.匹配包括换行在内的所有字符 re.M:多行匹配,影响 ^ 和 $

因此,我们的思路是,匹配<dt>总流量</dt></dd></dl>之间的内容。

改写以上匹配式子为:

1
2
3
4
5
6
7
8
def match_flows(str):
res = r'<dl class="dl-horizontal">(.*?)</dl>'
mm = re.findall(
res, str, re.S | re.M)
res=r'<dd>(.*?)</dd>'
mm= re.findall(
res, mm[0], re.S | re.M)
return mm

记录当前操作

首先

import logging

然后:

# 配置日志文件和日志级别
logging.basicConfig(filename='logger.log', level=logging.INFO)

nowtime=time.strftime('%Y-%m-%d',time.localtime(time.time()))#获取当前时间
str= nowtime+',\t总流量:'+lastFlows[0]+',\t已用流量:'+lastFlows[1]+',\t剩余流量:'+lastFlows[2]
logging.info(str)#写入日志

操作系统定期执行Python脚本

见参考文献4.

全部代码

 
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
#! /usr/bin/env python
# coding:utf-8
import sys
import re
import requests
import logging
import time
# 配置日志文件和日志级别
logging.basicConfig(filename='logger.log', level=logging.INFO)
def check(str):
hasCheckIn='<button id="checkin" class="btn btn-success btn-flat">'
noChecked='<a class="btn btn-success btn-flat disabled" href="#">'
yes=re.search(hasCheckIn,str)
if yes==None:
no=re.search(noChecked,str)
if no==None:
return -1 #什么都没找到
else:
return 0#找到了“不能签到”
else:
return 1#找到了“签到”
def match_flows(str):
res = r'<dl class="dl-horizontal">(.*?)</dl>'
mm = re.findall(
res, str, re.S | re.M)
res=r'<dd>(.*?)</dd>'
mm= re.findall(
res, mm[0], re.S | re.M)
return mm
## 这段代码是用于解决中文报错的问题
reload(sys)
sys.setdefaultencoding("utf8")
email='你的账号'
password='你的密码'
loginurl = 'https://ssr.0v0.loan/auth/login'
# 这行代码,是用来维持cookie的,你后续的操作都不用担心cookie,他会自动带上相应的cookie
s = requests.Session()
# 我们需要带表单的参数
loginparams={'email':email,'passwd':password,'remember_me':'ture'}
# post 数据实现登录
r = s.post(loginurl,data=loginparams)
# 验证是否登陆成功,抓取首页看看内容
r = s.get(loginurl)
res=check(r.content)#0=不能签到;1=可以签到;-1=什么都没找到;
if(res==1):#可以签到
checkinUrl="https://ssr.0v0.loan/user/checkin"
r=s.post(checkinUrl)
r = s.get(loginurl)
lastFlows=match_flows(r.content)
nowtime=time.strftime('%Y-%m-%d',time.localtime(time.time()))#获取当前时间
str= nowtime+',\t总流量:'+lastFlows[0]+',\t已用流量:'+lastFlows[1]+',\t剩余流量:'+lastFlows[2]
print str
logging.info(str)

参考文献

  1. 微信群分享:用Python模拟知乎自动登录
  2. Python正则表达式
  3. 常用正则表达式爬取网页信息及分析HTML标签总结
  4. 操作系统定期定时执行PYTHON脚本

签到成功后:

introduction部分

Bagging和boosting用途

Bagging和boosting主要是用多重预测来解决以下两个问题: 1. 在回归问题中获取最小误差; 2. 在分类问题中获取最小错误率。

Bagging和Boosting共性 都是通过训练不同的数据集来得到回归模型。

不同点

bagging 1. 从\(N_1\)个原始样本中,有放回地抽样(可能overlap)得到\(N_1\)个样本 2. 独立训练模型 3. 得到不同的预测 4. 求所有预测的平均得到最终结果

由于模型之间独立,因此可以对训练过程采取分布式或并行的方式。

boosting

模型是依次训练出来的。

  1. 从训练集中挑出\(N_1\)个样本,训练出第一个模型
  2. 挑出误差最大的样本
  3. 增大这些样本在下次被抽到的概率
  4. 再次进行训练,最终得到多个模型
  5. 最终对模型进行不同权重的加权,权重公式如下定义:

设已知训练集\((y_i,x_i),i=1,...,N_1\)。其中,\(x\)\(M\)维的向量,且\((y_i,x_i)\)唯一但未知(fixed but unknown) 我们将预测函数表示为\(y^{(p)}(x)\),则:

sample modeling error: \[PE=\frac{1}{N_2}\sum_{i=1}^{N_2}[y_i-y_i^{(p)}(x_i)]^2\] prediction error: \[ME=\frac{1}{N_2}\sum_{i=1}^{N_2}[y_i^{(t)}-y_i^{(p)}(x_i)]^2\]

其中, \(y_i^{(p)}(x_i)\)表示第\(i\)个测试集的预测值 \(y_i\)表示第\(i\)个测试集的观测值 \(y^{(t)}_i\)是实际值 \(y^{(p)}(x)\)的参数p是从\(N_2\)个测试集的观测值中获得的,但是上面的累加的式子中的\(y_i\)\(x_i\)是从从未被seen过的测试集的观测值\(N_2\)获得的。

看到这里突然发现有点跑偏了。决定先暂停这篇论文的研究。

增强学习

机器学习中的一个领域,关注智能体如何基于环境而采取一系列的行动,以取得最大化的预期利益或回报

特点

试错学习(Trail-and-error),由于没有直接的指导信息,智能体要以不断与环境进行交互,通过试错的方式来获得最佳策略。

延迟回报,增强学习的指导信息很少,而且往往是在事后(最后一个状态)才给出的,这就导致了一个问题,就是获得正回报或者负回报以后,如何将回报分配给前面的状态。

例子

比如下象棋,每一步都是一个决策过程,但决策的结果事后才知道 再比如机器人的行走,移动过程中不知道如何挪动 一种可行的思路是设计一个回报函数,每执行一步决策后,向agent进行汇报,比如四足机器人,如果他向前走了一步(接近目标),那么回报函数为正,后退为负。这样,我们对每一步进行评价,得到相应的回报函数,我们只需要找到一条回报值最大的路径(每步的回报之和最大),就认为是最佳的路径。

题目

编写代码,接收从屏幕输入的长度为16字节整数倍的字符串。回车后,按实例格式排版输出。

实例:

屏幕输入(均为可见字符): abcdefghigklmnopqrstuvwxyzabcdefghigklmnopqrstuvwxyzabcdefghigkl

代码

#include <iostream>
#include<cstring>
#include<cstdio>
using namespace std;
int main()
{
    //freopen("in.txt","r",stdin);
    char c[16];
    int count=0;
    int level=0;
    int temp=0;
    char in;
    while(true){
        c[count]=getchar();
        if(c[count]==-1){
            break;
        }
        if(count==16){
            printf("%08d",level*10);
            printf("  ");
            for(int i=0;i<16;i++){
                if(i==8){
                    printf(" ");
                }
                temp=c[i]+16*level;
                printf("%02X",c[i]);
                printf(" ");
            }
            printf(" ");
            for(int i=0;i<16;i++){
                std::cout<<c[i];
            }
            printf("\n");
            ++level;
            count=0;
        }
        ++count;
    }
    return 0;
}

本文是2017年北京邮电大学第十一届程序设计竞赛网络热身赛的F. Simple recursion题解。

题目

题目描述 r_clover shows you a BIIIIIIIIIIG water problem: if \(f(0)=1,f(1)=1\),\(f(n)=2f(n-1)+3f(n-2)\), what is \(f(n)mod1000000007\)?

输入格式 A single integer n(0≤n≤10000000000).

输出格式 A single integer, which is the answer for f(n)mod1000000007.

输入样例 10 输出样例 29525

思路

一开始想的是用递归。但经测试发现递归的时间、空间复杂度都太高,导致超时。

正确打开方式:求出递推关系,并用快速幂取模方法计算结果。

递推关系求解

《组合数学》内容————常系数线性齐次递推关系的求解。公式见后面的附1。 求解题目中所给的递推关系: \[f(n)=2f(n-1)+3f(n-2)\] \[f(0)=1,f(1)=1\] 它的特征方程为: \[x^2-2x-3=0\] 解方程得: \[x_1=3,x_2=-1\] 所以递推关系的通解为: \[f(n)=c_13^n+c_2(-1)^n\] 代入初值\(f(0)=1,f(1)=1\),得到: \[f(0)=c_1+c_2=1\] \[f(1)=3c_1-c_2=1\] 解得:\(c_1=c_2=0.5\) 因此该递推关系的解为\[f(n)=0.5(3)^n+0.5(-1)^n\]

代码实现:

1
2
3
4
5
6
7
8
9
10
long f(long long n,int modNum){
long long a=3;
long long b=1;
a= quick_algorithm(a,n,modNum);//(a的n次方)mod(modNum),是快速求幂取模的方法,详细见后面的推导
if(n%2==1){
b=-1;
}
a=0.5*a+0.5*b;
return a;
}

常系数线性齐次递归关系的求解

递推关系:

\[f(n)=c_1f(n-1)+c_2f(n-2)+...+c_kf(n-k)\]

递推关系的特征方程

\[x^k-c_1x^{k-1}-c_2x^{k-2}-...-c_k=0\]

递归关系的特征根:

特征方程的k个根(可能重根):\[q_1,q_2,...,q_k\]

递归关系的特解:

设q是非零复数,则\(f(n)=q^n\)是递推关系的解,当且仅当q是它的特征根。

递归关系的通解:

\(q_1,q_2,...,q_k\)是递推关系的k个互不相等的特征根,则 \[f(n)=b_1q_1^n+b_2q_2^n+b_3q_3^n+...+b_kq_k^n\]是递推关系的通解。

快速大数幂运算

这是一种很神奇的方法。

首先引入一些基础知识。

幂取模运算的一个性质

\[(a\times b)mod(c)=(((a)mod(c))\times ((b) mod(c)))mod(c)\]

大数幂运算 核心思想:将大数的幂运算拆解成了相对应的乘法运算,利用上面的式子,始终将我们的运算的数据量控制在c的范围以下。

对于目标: \[a^bmodc\] 我们将b表示为二进制: \[b=b_0+b_12+b_22^2+...+b_n2^n\] 那么, \[a^b=a^b_1\times a^{b_22^2} \times a^{b_32^3} \times ... \times a^{b_n2^n}\] 其中\(b_k\)要么为0,要么为1 去掉所有的0后,我们得到 \[a^b=a^{b_k2^k} \times ... \times a^{b_n2^n}\] 上式的模可以表示为: \[a^b mod c =a^{b_k2^k} mod c\times ... \times a^{b_n2^n}mod c\] 易发现: \[A_k=a^{b_k2^k} mod c\] \[...\] \[A_n=a^{b_n2^n} mod c\]

即:\(A(n)\)\(A(n-1)\)的平方倍!

实现:

1
2
3
4
5
6
7
8
9
10
11
12
int quick(int a,int b,int c)
{
int ans=1; //记录结果
a=a%c; //预处理,使得a处于c的数据范围之下
while(b!=0)
{
if(b&1) ans=(ans*a)%c; //如果b的二进制位不是0,那么我们的结果是要参与运算的
b>>=1; //二进制的移位操作,相当于每次除以2,用二进制看,就是我们不断的遍历b的二进制位
a=(a*a)%c; //不断的加倍
}
return ans;
}

代码

#include <iostream>
#include<cstdio>
#include<cmath>
#include<cstdlib>
#include<cstring>
#include<string>
#include<algorithm>
#include <math.h>
using namespace std;
long quick_algorithm(long long a,long long n,int modNum){
    a=a%modNum ;
    long long ans=1 ;
    //这里我们不需要考虑b<0,因为分数没有取模运算
    while(n!=0){
        if(n&1)
            ans=(ans*a)%modNum ;
        n>>=1  ;
        a=(a*a)%modNum;
    }
    return ans;
}
long set2(long long n,int modNum){
    long long a=3;
    long long b=1;
    /*for(long long i=0;i<n;i++){
        a=a*3;
        a=a%modNum;
    }*/
    a= quick_algorithm(a,n,modNum);
    if(n%2==1){
        b=-1;
    }
    //a=a;
    a=0.5*a+0.5*b;
    return a;
}

int main()
{
    int modNum=1000000007;
    long long input=999999999;
    scanf("%lld",&input);
    if(input==0||input==1){
        printf("%lld\n",1);
    }else{
        long long out=set2(input,modNum);
        printf("%lld\n",out);
    }
    return 0;
}

参考文献

  1. 《组合数学引论》

附上本题的艰辛史。