关于triplet loss的原理。目标函数和梯度推导在上一篇博客中已经讲过了。详细见:triplet loss原理以及梯度推导。这篇博文主要是讲caffe下实现triplet loss。编程菜鸟。假设有写的不优化的地方,欢迎指出。

1.怎样在caffe中添加新的layer

新版的caffe中添加新的layer。变得轻松多了。概括说来。分四步:
1)在./src/caffe/proto/caffe.proto 中添加 相应layer的paramter message。
2)在./include/caffe/***layers.hpp中添加该layer的类的声明。***表示有common_layers.hpp, data_layers.hpp, neuron_layers.hpp, vision_layers.hpp 和loss_layers.hpp等。
3)在./src/caffe/layers/文件夹下新建.cpp和.cu文件,进行类实现。
4)在./src/caffe/gtest/中添加layer的測试代码。对所写的layer前传和反传进行測试,測试还包含速度。
最后一步非常多人省了,或者没意识到。可是为保证代码正确,建议还是严格进行測试,磨刀不误砍柴功。


2.caffe中实现triplet loss layer


1.caffe.proto中添加triplet loss layer的定义

首先在message LayerParameter中追加 optional TripletLossParameter triplet_loss_param = 138; 当中138是我眼下LayerParameter message中现有元素的个数,详细是多少。能够看LayerParameter message上面凝视中的:
//LayerParameter next available layer-specific ID: 134 (last added: reshape_param)

然后添加Message:

message TripletLossParameter {
// margin for dissimilar pair
optional float margin = 1 [default = 1.0];
}

当中 margin就是定义triplet loss原理以及梯度推导所讲的alpha。


2.在./include/caffe/loss_layers.hpp中添加triplet loss layer的类的声明

详细解释见凝视。基本的是定义了一些变量。用来在前传中存储中间计算结果。以便在反传的时候避免反复计算。


/**
* @brief Computes the triplet loss
*/
template <typename Dtype>
class TripletLossLayer : public LossLayer<Dtype> {
public:
explicit TripletLossLayer(const LayerParameter& param)
: LossLayer<Dtype>(param){}
virtual void LayerSetUp(const vector<Blob<Dtype>*>& bottom,
const vector<Blob<Dtype>*>& top); virtual inline int ExactNumBottomBlobs() const { return 4; }
virtual inline const char* type() const { return "TripletLoss"; }
/**
* Unlike most loss layers, in the TripletLossLayer we can backpropagate
* to the first three inputs.
*/
virtual inline bool AllowForceBackward(const int bottom_index) const {
return bottom_index != 3;
} protected:
virtual void Forward_cpu(const vector<Blob<Dtype>*>& bottom,
const vector<Blob<Dtype>*>& top);
virtual void Forward_gpu(const vector<Blob<Dtype>*>& bottom,
const vector<Blob<Dtype>*>& top); virtual void Backward_cpu(const vector<Blob<Dtype>*>& top,
const vector<bool>& propagate_down, const vector<Blob<Dtype>*>& bottom);
virtual void Backward_gpu(const vector<Blob<Dtype>*>& top,
const vector<bool>& propagate_down, const vector<Blob<Dtype>*>& bottom); Blob<Dtype> diff_ap_; // cached for backward pass
Blob<Dtype> diff_an_; // cached for backward pass
Blob<Dtype> diff_pn_; // cached for backward pass Blob<Dtype> diff_sq_ap_; // cached for backward pass
Blob<Dtype> diff_sq_an_; // tmp storage for gpu forward pass Blob<Dtype> dist_sq_ap_; // cached for backward pass
Blob<Dtype> dist_sq_an_; // cached for backward pass Blob<Dtype> summer_vec_; // tmp storage for gpu forward pass
Blob<Dtype> dist_binary_; // tmp storage for gpu forward pass
};

3. 在./src/caffe/layers/文件夹下新建triplet_loss_layer.cpp,实现类

主要实现三个功能:
LayerSetUp:主要是做一些CHECK工作,然后依据bottom和top对类中的数据成员初始化。

Forward_cpu:前传。计算loss
Backward_cpu:反传,计算梯度。
/*
* triplet_loss_layer.cpp
*
* Created on: Jun 2, 2015
* Author: tangwei
*/ #include <algorithm>
#include <vector> #include "caffe/layer.hpp"
#include "caffe/loss_layers.hpp"
#include "caffe/util/io.hpp"
#include "caffe/util/math_functions.hpp" namespace caffe { template <typename Dtype>
void TripletLossLayer<Dtype>::LayerSetUp(
const vector<Blob<Dtype>*>& bottom, const vector<Blob<Dtype>*>& top) {
LossLayer<Dtype>::LayerSetUp(bottom, top);
CHECK_EQ(bottom[0]->num(), bottom[1]->num());
CHECK_EQ(bottom[1]->num(), bottom[2]->num());
CHECK_EQ(bottom[0]->channels(), bottom[1]->channels());
CHECK_EQ(bottom[1]->channels(), bottom[2]->channels());
CHECK_EQ(bottom[0]->height(), 1);
CHECK_EQ(bottom[0]->width(), 1);
CHECK_EQ(bottom[1]->height(), 1);
CHECK_EQ(bottom[1]->width(), 1);
CHECK_EQ(bottom[2]->height(), 1);
CHECK_EQ(bottom[2]->width(), 1); CHECK_EQ(bottom[3]->channels(),1);
CHECK_EQ(bottom[3]->height(), 1);
CHECK_EQ(bottom[3]->width(), 1); diff_ap_.Reshape(bottom[0]->num(), bottom[0]->channels(), 1, 1);
diff_an_.Reshape(bottom[0]->num(), bottom[0]->channels(), 1, 1);
diff_pn_.Reshape(bottom[0]->num(), bottom[0]->channels(), 1, 1); diff_sq_ap_.Reshape(bottom[0]->num(), bottom[0]->channels(), 1, 1);
diff_sq_an_.Reshape(bottom[0]->num(), bottom[0]->channels(), 1, 1);
dist_sq_ap_.Reshape(bottom[0]->num(), 1, 1, 1);
dist_sq_an_.Reshape(bottom[0]->num(), 1, 1, 1);
// vector of ones used to sum along channels
summer_vec_.Reshape(bottom[0]->channels(), 1, 1, 1);
for (int i = 0; i < bottom[0]->channels(); ++i)
summer_vec_.mutable_cpu_data()[i] = Dtype(1);
dist_binary_.Reshape(bottom[0]->num(), 1, 1, 1);
for (int i = 0; i < bottom[0]->num(); ++i)
dist_binary_.mutable_cpu_data()[i] = Dtype(1);
} template <typename Dtype>
void TripletLossLayer<Dtype>::Forward_cpu(
const vector<Blob<Dtype>*>& bottom,
const vector<Blob<Dtype>*>& top) {
int count = bottom[0]->count();
const Dtype* sampleW = bottom[3]->cpu_data();
caffe_sub(
count,
bottom[0]->cpu_data(), // a
bottom[1]->cpu_data(), // p
diff_ap_.mutable_cpu_data()); // a_i-p_i
caffe_sub(
count,
bottom[0]->cpu_data(), // a
bottom[2]->cpu_data(), // n
diff_an_.mutable_cpu_data()); // a_i-n_i
caffe_sub(
count,
bottom[1]->cpu_data(), // p
bottom[2]->cpu_data(), // n
diff_pn_.mutable_cpu_data()); // p_i-n_i
const int channels = bottom[0]->channels();
Dtype margin = this->layer_param_.triplet_loss_param().margin(); Dtype loss(0.0);
for (int i = 0; i < bottom[0]->num(); ++i) {
dist_sq_ap_.mutable_cpu_data()[i] = caffe_cpu_dot(channels,
diff_ap_.cpu_data() + (i*channels), diff_ap_.cpu_data() + (i*channels));
dist_sq_an_.mutable_cpu_data()[i] = caffe_cpu_dot(channels,
diff_an_.cpu_data() + (i*channels), diff_an_.cpu_data() + (i*channels));
Dtype mdist = sampleW[i]*std::max(margin + dist_sq_ap_.cpu_data()[i] - dist_sq_an_.cpu_data()[i], Dtype(0.0));
loss += mdist;
if(mdist==Dtype(0)){
//dist_binary_.mutable_cpu_data()[i] = Dtype(0);
//prepare for backward pass
caffe_set(channels, Dtype(0), diff_ap_.mutable_cpu_data() + (i*channels));
caffe_set(channels, Dtype(0), diff_an_.mutable_cpu_data() + (i*channels));
caffe_set(channels, Dtype(0), diff_pn_.mutable_cpu_data() + (i*channels));
}
}
loss = loss / static_cast<Dtype>(bottom[0]->num()) / Dtype(2);
top[0]->mutable_cpu_data()[0] = loss;
} template <typename Dtype>
void TripletLossLayer<Dtype>::Backward_cpu(const vector<Blob<Dtype>*>& top,
const vector<bool>& propagate_down, const vector<Blob<Dtype>*>& bottom) {
//Dtype margin = this->layer_param_.contrastive_loss_param().margin();
const Dtype* sampleW = bottom[3]->cpu_data();
for (int i = 0; i < 3; ++i) {
if (propagate_down[i]) {
const Dtype sign = (i < 2) ? -1 : 1;
const Dtype alpha = sign * top[0]->cpu_diff()[0] /
static_cast<Dtype>(bottom[i]->num());
int num = bottom[i]->num();
int channels = bottom[i]->channels();
for (int j = 0; j < num; ++j) {
Dtype* bout = bottom[i]->mutable_cpu_diff();
if (i==0) { // a
//if(dist_binary_.cpu_data()[j]>Dtype(0)){
caffe_cpu_axpby(
channels,
alpha*sampleW[j],
diff_pn_.cpu_data() + (j*channels),
Dtype(0.0),
bout + (j*channels));
//}else{
// caffe_set(channels, Dtype(0), bout + (j*channels));
//}
} else if (i==1) { // p
//if(dist_binary_.cpu_data()[j]>Dtype(0)){
caffe_cpu_axpby(
channels,
alpha*sampleW[j],
diff_ap_.cpu_data() + (j*channels),
Dtype(0.0),
bout + (j*channels));
//}else{
// caffe_set(channels, Dtype(0), bout + (j*channels));
//}
} else if (i==2) { // n
//if(dist_binary_.cpu_data()[j]>Dtype(0)){
caffe_cpu_axpby(
channels,
alpha*sampleW[j],
diff_an_.cpu_data() + (j*channels),
Dtype(0.0),
bout + (j*channels));
//}else{
// caffe_set(channels, Dtype(0), bout + (j*channels));
//}
}
} // for num
} //if propagate_down[i]
} //for i
} #ifdef CPU_ONLY
STUB_GPU(TripletLossLayer);
#endif INSTANTIATE_CLASS(TripletLossLayer);
REGISTER_LAYER_CLASS(TripletLoss); } // namespace caffe

4.在./src/caffe/layers/文件夹下新建triplet_loss_layer.cu,实现GPU下的前传和反传

在GPU下实现前传和反传
/*
* triplet_loss_layer.cu
*
* Created on: Jun 2, 2015
* Author: tangwei
*/ #include <algorithm>
#include <vector> #include "caffe/layer.hpp"
#include "caffe/util/io.hpp"
#include "caffe/util/math_functions.hpp"
#include "caffe/vision_layers.hpp" namespace caffe { template <typename Dtype>
void TripletLossLayer<Dtype>::Forward_gpu(
const vector<Blob<Dtype>*>& bottom, const vector<Blob<Dtype>*>& top) {
const int count = bottom[0]->count();
caffe_gpu_sub(
count,
bottom[0]->gpu_data(), // a
bottom[1]->gpu_data(), // p
diff_ap_.mutable_gpu_data()); // a_i-p_i
caffe_gpu_sub(
count,
bottom[0]->gpu_data(), // a
bottom[2]->gpu_data(), // n
diff_an_.mutable_gpu_data()); // a_i-n_i
caffe_gpu_sub(
count,
bottom[1]->gpu_data(), // p
bottom[2]->gpu_data(), // n
diff_pn_.mutable_gpu_data()); // p_i-n_i caffe_gpu_powx(
count,
diff_ap_.mutable_gpu_data(), // a_i-p_i
Dtype(2),
diff_sq_ap_.mutable_gpu_data()); // (a_i-p_i)^2
caffe_gpu_gemv(
CblasNoTrans,
bottom[0]->num(),
bottom[0]->channels(),
Dtype(1.0), //alpha
diff_sq_ap_.gpu_data(), // (a_i-p_i)^2 // A
summer_vec_.gpu_data(), // x
Dtype(0.0), //belta
dist_sq_ap_.mutable_gpu_data()); // \Sum (a_i-p_i)^2 //y caffe_gpu_powx(
count,
diff_an_.mutable_gpu_data(), // a_i-n_i
Dtype(2),
diff_sq_an_.mutable_gpu_data()); // (a_i-n_i)^2
caffe_gpu_gemv(
CblasNoTrans,
bottom[0]->num(),
bottom[0]->channels(),
Dtype(1.0), //alpha
diff_sq_an_.gpu_data(), // (a_i-n_i)^2 // A
summer_vec_.gpu_data(), // x
Dtype(0.0), //belta
dist_sq_an_.mutable_gpu_data()); // \Sum (a_i-n_i)^2 //y Dtype margin = this->layer_param_.triplet_loss_param().margin();
Dtype loss(0.0);
const Dtype* sampleW = bottom[3]->cpu_data();
for (int i = 0; i < bottom[0]->num(); ++i) {
loss += sampleW[i]*std::max(margin +dist_sq_ap_.cpu_data()[i]- dist_sq_an_.cpu_data()[i], Dtype(0.0));
}
loss = loss / static_cast<Dtype>(bottom[0]->num()) / Dtype(2);
top[0]->mutable_cpu_data()[0] = loss;
} template <typename Dtype>
__global__ void CLLBackward(const int count, const int channels,
const Dtype margin, const Dtype alpha, const Dtype* sampleW,
const Dtype* diff, const Dtype* dist_sq_ap_, const Dtype* dist_sq_an_,
Dtype *bottom_diff) {
CUDA_KERNEL_LOOP(i, count) {
int n = i / channels; // the num index, to access dist_sq_ap_ and dist_sq_an_
Dtype mdist(0.0);
mdist = margin +dist_sq_ap_[n] - dist_sq_an_[n];
if (mdist > 0.0) {
bottom_diff[i] = alpha*sampleW[n]*diff[i];
} else {
bottom_diff[i] = 0;
}
}
} template <typename Dtype>
void TripletLossLayer<Dtype>::Backward_gpu(const vector<Blob<Dtype>*>& top,
const vector<bool>& propagate_down, const vector<Blob<Dtype>*>& bottom) {
Dtype margin = this->layer_param_.triplet_loss_param().margin();
const int count = bottom[0]->count();
const int channels = bottom[0]->channels(); for (int i = 0; i < 3; ++i) {
if (propagate_down[i]) {
const Dtype sign = (i < 2) ? -1 : 1;
const Dtype alpha = sign * top[0]->cpu_diff()[0] /
static_cast<Dtype>(bottom[0]->num());
if(i==0){
// NOLINT_NEXT_LINE(whitespace/operators)
CLLBackward<Dtype><<<CAFFE_GET_BLOCKS(count), CAFFE_CUDA_NUM_THREADS>>>(
count, channels, margin, alpha,
bottom[3]->gpu_data(),
diff_pn_.gpu_data(), // the cached eltwise difference between p and n
dist_sq_ap_.gpu_data(), // the cached square distance between a and p
dist_sq_an_.gpu_data(), // the cached square distance between a and n
bottom[i]->mutable_gpu_diff());
CUDA_POST_KERNEL_CHECK;
}else if(i==1){
// NOLINT_NEXT_LINE(whitespace/operators)
CLLBackward<Dtype><<<CAFFE_GET_BLOCKS(count), CAFFE_CUDA_NUM_THREADS>>>(
count, channels, margin, alpha,
bottom[3]->gpu_data(),
diff_ap_.gpu_data(), // the cached eltwise difference between a and p
dist_sq_ap_.gpu_data(), // the cached square distance between a and p
dist_sq_an_.gpu_data(), // the cached square distance between a and n
bottom[i]->mutable_gpu_diff());
CUDA_POST_KERNEL_CHECK;
}else if(i==2){
// NOLINT_NEXT_LINE(whitespace/operators)
CLLBackward<Dtype><<<CAFFE_GET_BLOCKS(count), CAFFE_CUDA_NUM_THREADS>>>(
count, channels, margin, alpha,
bottom[3]->gpu_data(),
diff_an_.gpu_data(), // the cached eltwise difference between a and n
dist_sq_ap_.gpu_data(), // the cached square distance between a and p
dist_sq_an_.gpu_data(), // the cached square distance between a and n
bottom[i]->mutable_gpu_diff());
CUDA_POST_KERNEL_CHECK; }
}
}
} INSTANTIATE_LAYER_GPU_FUNCS(TripletLossLayer); } // namespace caffe

5. 在./src/caffe/test/文件夹下添加test_triplet_loss_layer.cpp

/*
* test_triplet_loss_layer.cpp
*
* Created on: Jun 3, 2015
* Author: tangwei
*/ #include <algorithm>
#include <cmath>
#include <cstdlib>
#include <cstring>
#include <vector> #include "gtest/gtest.h" #include "caffe/blob.hpp"
#include "caffe/common.hpp"
#include "caffe/filler.hpp"
#include "caffe/vision_layers.hpp" #include "caffe/test/test_caffe_main.hpp"
#include "caffe/test/test_gradient_check_util.hpp" namespace caffe { template <typename TypeParam>
class TripletLossLayerTest : public MultiDeviceTest<TypeParam> {
typedef typename TypeParam::Dtype Dtype; protected:
TripletLossLayerTest()
: blob_bottom_data_i_(new Blob<Dtype>(512, 2, 1, 1)),
blob_bottom_data_j_(new Blob<Dtype>(512, 2, 1, 1)),
blob_bottom_data_k_(new Blob<Dtype>(512, 2, 1, 1)),
blob_bottom_y_(new Blob<Dtype>(512, 1, 1, 1)),
blob_top_loss_(new Blob<Dtype>()) {
// fill the values
FillerParameter filler_param;
filler_param.set_min(-1.0);
filler_param.set_max(1.0); // distances~=1.0 to test both sides of margin
UniformFiller<Dtype> filler(filler_param);
filler.Fill(this->blob_bottom_data_i_);
blob_bottom_vec_.push_back(blob_bottom_data_i_);
filler.Fill(this->blob_bottom_data_j_);
blob_bottom_vec_.push_back(blob_bottom_data_j_);
filler.Fill(this->blob_bottom_data_k_);
blob_bottom_vec_.push_back(blob_bottom_data_k_);
for (int i = 0; i < blob_bottom_y_->count(); ++i) {
blob_bottom_y_->mutable_cpu_data()[i] = caffe_rng_rand() % 2; // 0 or 1
}
blob_bottom_vec_.push_back(blob_bottom_y_);
blob_top_vec_.push_back(blob_top_loss_);
}
virtual ~TripletLossLayerTest() {
delete blob_bottom_data_i_;
delete blob_bottom_data_j_;
delete blob_bottom_data_k_;
delete blob_top_loss_;
} Blob<Dtype>* const blob_bottom_data_i_;
Blob<Dtype>* const blob_bottom_data_j_;
Blob<Dtype>* const blob_bottom_data_k_;
Blob<Dtype>* const blob_bottom_y_;
Blob<Dtype>* const blob_top_loss_;
vector<Blob<Dtype>*> blob_bottom_vec_;
vector<Blob<Dtype>*> blob_top_vec_;
}; TYPED_TEST_CASE(TripletLossLayerTest, TestDtypesAndDevices); TYPED_TEST(TripletLossLayerTest, TestForward) {
typedef typename TypeParam::Dtype Dtype;
LayerParameter layer_param;
TripletLossLayer<Dtype> layer(layer_param);
layer.SetUp(this->blob_bottom_vec_, this->blob_top_vec_);
layer.Forward(this->blob_bottom_vec_, this->blob_top_vec_);
// manually compute to compare
const Dtype margin = layer_param.triplet_loss_param().margin();
const int num = this->blob_bottom_data_i_->num();
const int channels = this->blob_bottom_data_i_->channels(); const Dtype *sampleW = this->blob_bottom_y_->cpu_data(); Dtype loss(0);
for (int i = 0; i < num; ++i) {
Dtype dist_sq_ij(0);
Dtype dist_sq_ik(0);
for (int j = 0; j < channels; ++j) {
Dtype diff_ij = this->blob_bottom_data_i_->cpu_data()[i*channels+j] -
this->blob_bottom_data_j_->cpu_data()[i*channels+j];
dist_sq_ij += diff_ij*diff_ij;
Dtype diff_ik = this->blob_bottom_data_i_->cpu_data()[i*channels+j] -
this->blob_bottom_data_k_->cpu_data()[i*channels+j];
dist_sq_ik += diff_ik*diff_ik;
}
loss += sampleW[i]*std::max(Dtype(0.0), margin+dist_sq_ij-dist_sq_ik);
}
loss /= static_cast<Dtype>(num) * Dtype(2);
EXPECT_NEAR(this->blob_top_loss_->cpu_data()[0], loss, 1e-6);
} TYPED_TEST(TripletLossLayerTest, TestGradient) {
typedef typename TypeParam::Dtype Dtype;
LayerParameter layer_param;
TripletLossLayer<Dtype> layer(layer_param);
layer.SetUp(this->blob_bottom_vec_, this->blob_top_vec_);
GradientChecker<Dtype> checker(1e-2, 1e-2, 1701);
// check the gradient for the first two bottom layers
checker.CheckGradientExhaustive(&layer, this->blob_bottom_vec_,
this->blob_top_vec_, 0);
checker.CheckGradientExhaustive(&layer, this->blob_bottom_vec_,
this->blob_top_vec_, 1);
} } // namespace caffe



3.编译測试

又一次 make all 假设出错,检查代码语法错误。
make test
make runtest 假设成功,全是绿色的OK  否则会给出红色提示。就得看看是不是实现逻辑上出错了。



怎样在caffe中添加layer以及caffe中triplet loss layer的实现的更多相关文章

  1. Tutorial: Triplet Loss Layer Design for CNN

    Tutorial:  Triplet Loss Layer Design for CNN Xiao Wang  2016.05.02 Triplet Loss Layer could be a tri ...

  2. 代码中添加事务控制 VS(数据库存储过程+事务) 保证数据的完整性与一致性

    做人事档案的系统考虑到数据的安全性与一致性,毕竟是要对外上线.真正投入使用的项目,数据库的可靠性与安全性上我们开发人员要考虑的就很多了,记得做机房收费系统时注册新卡是自己为了简单,写成了一个存储过程( ...

  3. JMeter怎么在get URL请求、POST请求中添加动态参数用于服务器段安全验证

    从前一个页面(含有服务器段返回给客户端的参数,用于在下一次请求时验证)中添加后置处理器中的Regular Expression Extractor,使用正则表达式对参数进行提取. 在用到这些变量时可以 ...

  4. C# 类库中添加注释方法

    C# 类库中添加注释方法 C#中新建的类库添加注释时,应注意以下问题: 1.编译动态类库时命名空间要规范,一般不要和类同名,命名空间一般定义格式:项目名+类文件名: 2.动态类库中,类.方法的注释都采 ...

  5. c#在panel或groupbox中添加窗体,实现点击不同按钮或combox时panel中窗体切换,在xtratabcontrol中添加窗体

    参考panel添加窗体: http://blog.csdn.net/illegalname/article/details/65444249 http://blog.csdn.net/Eastmoun ...

  6. 在weka中添加libSVM或者HMM等新算法

    转:http://kasy-13.blog.163.com/blog/static/8214691420143226365887/ Weka的全名是怀卡托智能分析环境(Waikato Environm ...

  7. 如何在VS2010的VC++ 基于对话框的MFC程序中添加菜单

    方法1:亲测 成功  转载自https://social.msdn.microsoft.com/Forums/vstudio/zh-CN/48338f6b-e5d9-4c0c-8b17-05ca3ef ...

  8. 给iOS项目中添加图片,并通过UIImageView引用和显示该UIImage图片

    [问题] 关于iOS/iPhone中的文件选择对话框,用于用户去选择图片等文件 过程中,问题转换为,需要给当前iOS项目中,添加一个图片. 类似于Windows开发中的资源文件,其中图片文件属于资源的 ...

  9. 在Controller中添加事务管理

    文章参考了此博客: https://blog.csdn.net/qq_40594137/article/details/82772545 写这篇文章之前先说明一下: 1. Controller中添加事 ...

随机推荐

  1. this和super的区别

    this和super的区别 1.this和super都代表什么 * this:代表当前对象的引用,谁来调用我,我就代表谁 * super:本类当前对象的父类内存空间标识 2.this和super的使用 ...

  2. Homework 3

    1. 是否需要有代码规范? 这些规范都是官僚制度下产生的浪费大家的编程时间.影响人们开发效率, 浪费时间的东西. (反对) 我是个艺术家,手艺人,我有自己的规范和原则.  (反对) 规范不能强求一律, ...

  3. VB中WinSock控件的属性、方法、事件及应用

    一.WinSock简介       Socket(套接字)最初是由加利福尼亚大学Berkeley(伯克利)分校为UNIX操作系统开发的网络通信接口,随着UNIX的广泛使用,Socket成为当前最流行的 ...

  4. 错误 1 未知的服务器标记“asp:ScriptManager”。

    如题 ... 解决方案 :将 <%@ Register Assembly="System.Web.Extensions, Version=1.0.61025.0, Culture=ne ...

  5. WEB服务器、应用程序服务器、HTTP服务器区别(转)

    WEB服务器.应用程序服务器.HTTP服务器有何区别?IIS.Apache.Tomcat.Weblogic.WebSphere都各属于哪种服务器,这些问题困惑了很久,今天终于梳理清楚了: Web服务器 ...

  6. nutch 采集到的数据与实际不符

    现象,这个网站我总计能抽取将近500个URL,但实际只抽取了100条 解析:nutch默认从一个页面解析出的链接,只取前 100 个. <property> <name>db. ...

  7. oracle dg坏境主库redolog改动大小

    --备库standby 主库四个redolog 曾经都是50M大小 SQL> alter database recover managed standby database cancel;   ...

  8. 转自 BotVS 量化平台社区 --- 如何从绩效报告中分辨出“好模型 or 坏模型”

    ## NO:01 在投资行业一直有一个说法,靠交易盈利是所有赚钱方式中最难的一种,以至于有人戏谑称交易为投资领域的搬砖工作. 为了提升交易效率.挖掘潜在投资机会,量化交易伴随着信息技术的发展大行其道, ...

  9. php操作excel表格的导入和导出

    前言:对于excel大家肯定熟悉不过了的,那么我们在日常的业务中应该是有对这些文件的导入导出操作的 类的下载:composer require phpoffice/phpexcel,其中Classes ...

  10. 实验一:c++简单程序设计(1)

    实验结论 编程练习2-28 switch版源码: #include <iostream> using namespace std; int main(void) { cout <&l ...