Resnet改进的色情图片检测-nsfw

 

nsfw(Not suitable for work classifier)是雅虎开源的进行色情检测识别的一个网络,该网络基于Resnet50修改,取得了非常不错的色情检测效果。

在安装caffe之后就可以运行上面的网络,运行shell脚本如下。

 

#!/usr/bin/env sh
python ./classify_nsfw.py \
 --model_defnsfw_model/deploy.prototxt \
 --pretrained_modelnsfw_model/resnet_50_1by2_nsfw.caffemodel \
 test.jpg

 

 

 

根据作者提供的python程序实现的基于c++的图形化显示,程序如下,

 

class Classifier {
public:
	Classifier(const string& model_file,const string& trained_file);
	float Classify(const cv::Mat& img);

private:
	std::vector<float> Predict(const cv::Mat& img);
	void WrapInputLayer(std::vector<cv::Mat>* input_channels);
	void Preprocess(const cv::Mat& img,	std::vector<cv::Mat>* input_channels);

private:
	shared_ptr<Net<float> > net_;
	cv::Size input_geometry_;
	int num_channels_;
	cv::Mat mean_;
};

Classifier::Classifier(const string& model_file,const string& trained_file) {
#ifdef CPU_ONLY
	Caffe::set_mode(Caffe::CPU);
#else
	Caffe::set_mode(Caffe::GPU);
#endif

	net_.reset(new Net<float>(model_file, TEST));
	net_->CopyTrainedLayersFrom(trained_file);

	CHECK_EQ(net_->num_inputs(), 1) << "Network should have exactly one input.";
	CHECK_EQ(net_->num_outputs(), 1) << "Network should have exactly one output.";

	Blob<float>* input_layer = net_->input_blobs()[0];
	num_channels_ = input_layer->channels();
	CHECK(num_channels_ == 3 || num_channels_ == 1)
		<< "Input layer should have 1 or 3 channels.";
	input_geometry_ = cv::Size(input_layer->width(), input_layer->height());

}

float Classifier::Classify(const cv::Mat& img) {
	std::vector<float> output = Predict(img);
	return output[1];
}


std::vector<float> Classifier::Predict(const cv::Mat& img) {
	Blob<float>* input_layer = net_->input_blobs()[0];
	input_layer->Reshape(1, num_channels_,
		input_geometry_.height, input_geometry_.width);
	net_->Reshape();

	std::vector<cv::Mat> input_channels;
	WrapInputLayer(&input_channels);

	Preprocess(img, &input_channels);

	net_->Forward();

	Blob<float>* output_layer = net_->output_blobs()[0];
	const float* begin = output_layer->cpu_data();
	const float* end = begin + output_layer->channels();
	return std::vector<float>(begin, end);
}

void Classifier::WrapInputLayer(std::vector<cv::Mat>* input_channels) {
	Blob<float>* input_layer = net_->input_blobs()[0];

	int width = input_layer->width();
	int height = input_layer->height();
	float* input_data = input_layer->mutable_cpu_data();
	for (int i = 0; i < input_layer->channels(); ++i) {
		cv::Mat channel(height, width, CV_32FC1, input_data);
		input_channels->push_back(channel);
		input_data += width * height;
	}
}

void Classifier::Preprocess(const cv::Mat& img,
	std::vector<cv::Mat>* input_channels) {
	cv::Mat sample;
	if (img.channels() == 3 && num_channels_ == 1)
		cv::cvtColor(img, sample, cv::COLOR_BGR2GRAY);
	else if (img.channels() == 4 && num_channels_ == 1)
		cv::cvtColor(img, sample, cv::COLOR_BGRA2GRAY);
	else if (img.channels() == 4 && num_channels_ == 3)
		cv::cvtColor(img, sample, cv::COLOR_BGRA2BGR);
	else if (img.channels() == 1 && num_channels_ == 3)
		cv::cvtColor(img, sample, cv::COLOR_GRAY2BGR);
	else
		sample = img;

	cv::Mat sample_resized;
	if (sample.size() != input_geometry_)
		cv::resize(sample, sample_resized, input_geometry_);
	else
		sample_resized = sample;

	cv::Mat sample_float;
	if (num_channels_ == 3)
		sample_resized.convertTo(sample_float, CV_32FC3);
	else
		sample_resized.convertTo(sample_float, CV_32FC1);
	
	vector<cv::Mat>channels_mean(3);
	channels_mean[0] = cv::Mat::ones(sample_float.rows, sample_float.cols, CV_32FC1) * 104;
	channels_mean[1] = cv::Mat::ones(sample_float.rows, sample_float.cols, CV_32FC1) * 117;
	channels_mean[2] = cv::Mat::ones(sample_float.rows, sample_float.cols, CV_32FC1) * 123;
	cv::merge(channels_mean, mean_);

	cv::Mat sample_normalized;
	cv::subtract(sample_float, mean_, sample_normalized);
	CHECK(reinterpret_cast<float*>(input_channels->at(0).data)
		== net_->input_blobs()[0]->cpu_data())
		<< "Input channels are not wrapping the input layer of the network.";
}

int main(int argc, char** argv) {

	::google::InitGoogleLogging(argv[0]);

	string model_file = "deploy.prototxt";
	string trained_file = "resnet_50_1by2_nsfw.caffemodel";
	
	Classifier classifier(model_file, trained_file);

	string file = "1.jpg";
	std::cout << "---------- Prediction for "<< file << " ----------" << std::endl;

	cv::Mat img = cv::imread(file, -1);
	CHECK(!img.empty()) << "Unable to decode image " << file;
	float prediction = classifier.Classify(img);
	char str_head[50] = "NSFW Score:";
	char str_pre[10];
	sprintf_s(str_pre, "%.4f", prediction);

	std::cout << "Scores < 0.2----->safe" << std::endl;
	std::cout << "Scores > 0.8----->NSFW" << std::endl;
	std::cout << "binned for different NSFW levels" << std::endl;
	std::cout << "Score:" << prediction << std::endl;
	
	cv::putText(img, std::strcat(str_head,str_pre), cv::Point(10, img.rows - 20), 3, 1, cv::Scalar(0, 0, 255));
	cv::imshow("result", img);
	cv::waitKey();
	return 0;

}

 

 

 

实验结果如下,

 

程序链接:http://download.youkuaiyun.com/detail/qq_14845119/9751834

reference

https://github.com/yahoo/open_nsfw

 

<think>好的,用户想了解ResNet-18、ResNet-34和ResNet-50之间的主要区别,特别是深度、参数量和性能方面。首先,我需要回忆一下ResNet的基本结构。ResNet的核心是残差块,分为两种类型:BasicBlock和Bottleneck。ResNet-18和ResNet-34使用的是BasicBlock,而更深的如ResNet-50则使用Bottleneck结构来减少参数量并提高效率。接下来,我需要确定每个模型的层数。ResNet-18有18层,ResNet-34有34层,ResNet-50有50层。这里的层数指的是带有权重的层,比如卷积层和全连接层,不包括池化层这样的无参数层。然后,参数量的计算需要考虑每个残差块中的卷积层数量和通道数。BasicBlock包含两个3x3卷积层,而Bottleneck包含1x1、3x3和1x1的卷积层,通过中间的1x1卷积来降维和升维,从而减少参数量。例如,ResNet-18的每个残差块有两个3x3卷积层,假设输入输出通道都是64,那么参数量是(3x3x64x64) +(3x3x64x64) =两个卷积层的参数。而Bottleneck结构中的参数量计算会更复杂,但总体上会更节省参数,因为中间的3x3卷积的通道数被减少了,比如先通过1x1卷积降维到64,再3x3卷积处理,再通过1x1升维到256,这样总的参数量可能更少。性能方面,更深的网络通常有更高的准确率,但参数量和计算量也会增加。不过ResNet通过残差结构缓解了深度增加带来的梯度问题,所以ResNet-50在ImageNet上的表现应该比ResNet-18更好。但需要具体的数据支持,比如Top-1和Top-5准确率。同时,参数量方面,ResNet-50的参数量大约是25.6百万,而ResNet-18是11.7百万,ResNet-34大约是21.8百万。还需要注意用户可能混淆深度和层数的关系,比如ResNet-50虽然层数多,但由于Bottleneck结构,实际参数量的增长并不是线性的。另外,用户可能关心实际应用中的选择,比如在计算资源有限的情况下,ResNet-18可能更适合,而需要更高准确率时选择ResNet-50。 最后,生成相关问题时要围绕区别、应用场景、结构选择等方面展开,确保问题有针对性,帮助用户深入理解。</think>### ResNet-18、ResNet-34与ResNet-50的区别分析 #### 1. **网络深度与结构差异** - **ResNet-18**:共18层(包含17个卷积层和1个全连接层),使用$2 \times 2$个**BasicBlock**残差块(每个块包含2个$3 \times 3$卷积层)[^1]。 - **ResNet-34**:共34层,结构与ResNet-18类似,但残差块数量更多,同样基于BasicBlock。 - **ResNet-50**:共50层,采用**Bottleneck**残差块(每个块包含1个$1 \times 1$降维卷积、1个$3 \times 3$卷积和1个$1 \times 1$升维卷积),显著减少计算量[^2]。 $$ \text{Bottleneck参数量公式:} C_{in} \times 1 \times 1 \times C_{mid} + C_{mid} \times 3 \times 3 \times C_{mid} + C_{mid} \times 1 \times 1 \times C_{out} $$ #### 2. **参数量对比** | 模型 | 参数量(百万) | 关键结构特点 | |------------|----------------|----------------------------------| | ResNet-18 | 11.7M | BasicBlock堆叠,通道数较少 | | ResNet-34 | 21.8M | BasicBlock数量翻倍 | | ResNet-50 | 25.6M | Bottleneck设计,通道数更高但更高效 | #### 3. **性能表现** - **准确率**(ImageNet数据集): - ResNet-18:Top-1准确率约$70\%$,Top-5约$89\%$ - ResNet-50:Top-1准确率约$76\%$,Top-5约$93\%$ - **计算效率**:ResNet-50的Bottleneck结构通过降维减少计算量(如$1 \times 1$卷积压缩通道数),在相似参数量下比ResNet-34更高效[^2]。 #### 4. **应用场景** - **轻量级任务**(移动端/边缘计算):优先选择ResNet-18 - **平衡性能与资源**:ResNet-34适合中等计算资源场景 - **高精度需求**(如医学图像分析):ResNet-50表现更优 ---
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值