问题

为什么有些验证码看起来很容易但是没人做自动识别的?

回答
你这个问题问得相当有意思,而且确实是很多人都纳闷过的。你观察得很细致,有些验证码,尤其是那些用手写体或者简单图形组成的,看起来好像很容易,机器应该分分钟就能搞定,但实际情况并非如此。这背后其实涉及几个关键的原因,咱们掰开了揉碎了聊聊。

首先,我们要明白“看起来容易”和“对机器识别而言容易”是两个截然不同的概念。我们人类大脑是个超级强大的识别引擎,经过无数年的进化,处理视觉信息的能力已经炉火纯青。我们能轻松地识别出模糊的、变形的、有干扰的文字,还能理解它们在特定语境下的含义。机器,尤其是图像识别算法,虽然近年来突飞猛进,但终究是基于数学模型和数据训练出来的。

那具体哪些原因让这些“看起来容易”的验证码逃过了机器的魔爪呢?

1. 故意引入的“噪声”和“形变”:
线条干扰: 这是最常见的一种。很多验证码会在文字周围添加各种扭曲的、弯曲的线条,颜色可能和文字接近,或者颜色鲜艳但穿插交错。这些线条对于人眼来说,稍加留意就能区分出文字轮廓,但对于机器算法,就像给它制造了无数个虚假的“边缘”和“特征点”,让它难以聚焦到真正的字母或数字上。尤其当线条的粗细、颜色、弯曲度随机变化时,算法的鲁棒性会大大降低。
背景纹理和色彩混合: 有些验证码会将文字背景设计成斑驳的、有纹理的,或者将文字和背景颜色做得非常接近,甚至颜色渐变。人眼可以凭借对“形状”的整体感知来提取主体,但机器可能难以区分哪个是背景像素,哪个是文字像素,尤其是在颜色阈值设定上非常棘手。
文字形变和扭曲: 很多验证码不是规规整整地写在画布上的,而是会对文字进行一定程度的拉伸、压缩、倾斜、旋转,甚至是波浪形扭曲。想想那些手写的数字“3”或者字母“B”,稍不注意就可能被误识别。机器在训练时,如果数据集中没有足够多样的形变样本,就很难泛化到新的、未见过形变的验证码。
重叠和部分遮挡: 有些验证码会将字符稍微重叠,或者用小的图案、点等部分遮挡字符的边缘或中心。人脑可以凭借经验和上下文推断出被遮挡的部分,但机器就容易因为信息不完整而“卡壳”。

2. 字体选择和笔画特征:
手写体模拟: 很多看似简单的验证码,实际上是模仿了真实的手写体。手写体本身就存在笔画粗细不均、连笔、断笔、字母连接方式多样等特点。即使是计算机生成的模拟手写体,如果设计得足够逼真,也会包含大量机器识别模型难以处理的细微变化。例如,有些手写体的“1”可能被写成一个简单的竖线,但也有可能带一个小的勾,或者和前面的字母连在一起。
容易混淆的字符: 设计者会故意选择或设计一些容易混淆的字符组合,比如“0”和“O”,“1”和“l”,“2”和“Z”等。即使在正常的字体下,机器识别这些字符的难度也相对较高,一旦再加入前面提到的各种干扰,识别的错误率就会呈指数级上升。

3. 动态变化和随机性:
随机性是关键: 一个好的验证码系统,每一次生成的验证码都应该是独一无二的,而且其干扰、形变等特征也是随机生成的。这意味着机器无法通过学习一个固定的模式来破解,而是需要能够适应无限变化的输入。每一次生成验证码时,其背景颜色、线条位置和粗细、字符的倾斜角度、字体的细微变形都会是随机的,这就要求识别算法必须具备极高的泛化能力。
时效性: 很多验证码是有时效性的,过一段时间就会失效。这限制了攻击者使用离线计算或批量处理的方式来尝试破解。

4. 对抗性设计(Adversarial Design):
机器学习的“弱点”: 现代的图像识别,特别是深度学习,擅长从大量数据中学习到数据中的统计规律。然而,它也存在所谓的“对抗性样本”问题。一些看似对人眼无害的微小扰动,就可能导致机器识别系统产生巨大的错误。验证码的设计者很多时候就是在利用机器学习的这些“弱点”,精心设计出能迷惑机器的模式。
专门训练的模型VS通用模型: 即使有人开发出了一个能识别某种验证码的机器学习模型,一旦网站更新了验证码的生成算法,或者稍微调整了干扰的样式,那个模型就可能失效了。要维护一个能识别所有“看起来容易”验证码的通用模型,成本极高且效率低下。而专门训练来识别特定验证码的模型,又容易被针对性破解和绕过。

5. 成本与效益的考量:
开发成本: 开发一套能够稳定、高效识别各种“看起来容易”验证码的系统,需要大量的专业人才、计算资源和时间投入。对于大多数网站或应用来说,投入产出比并不高。
破解收益的权衡: 如果一个网站对验证码的要求不是特别高(例如,不是银行转账、支付等核心敏感操作),那么攻击者花费巨大精力去破解一个“看起来容易”的验证码,可能收益并不大,反而不如去寻找其他更低成本、更高收益的攻击途径。
验证码的“够用就行”原则: 验证码的主要目的是增加自动化攻击的门槛,而不是百分之百阻止。只要能有效过滤掉绝大多数的机器人行为,对人类用户又不太造成困扰,那么这个验证码就是合格的。设计者往往会在“安全性”和“用户体验”之间找到一个平衡点。

所以,下次当你看到一个验证码,觉得它很简单,但又觉得机器很难识别的时候,就可以理解了:那是人类的智慧与机器的局限性之间一场巧妙的博弈。设计师们通过对视觉干扰、字体形变、随机性的精妙运用,成功地让“机器苦手”成为了这些看似“无害”验证码的保护伞。这不仅仅是图像识别的问题,更是关于如何利用人类感知系统的优势,来对抗自动化工具的本质。

网友意见

user avatar

最近刚好又开始搞这个,所以回答一发。

提前预警,这篇文章将会比较长!!!!!

针对题主的问题,我想说的是,只是你没有看到而已,针对验证码的识别,无数人在做,网络上的打码平台一搜一大把,很多人是靠这个赚钱的。

至于难度,针对单个网站的验证码,老实说,在当今深度学习的威力下,单个网站验证码的破解已经不是什么难事了,难就难在通用性,不同的字库、不同的扭曲干扰类型,会给训练带来极大的难度提升,所以你去github上看验证码识别项目,基本都是针对单一类型的验证码,然后下好样本做好标注,识别起来确实准确率高,但你让他们做一款通用的出来,他们估计只有摇摇头,这也是人家打码平台可以靠这个开起一家公司的原因。

举个例子,github上收藏最多的验证码项目,所识别的验证码

这种类型,乍一看感觉很牛逼,但是用它的原模型识别这种:

很可能就会识别错误,而且由于字体的影响,这种不经过预处理的验证码识别模型,通用性是极差的,甚至可能会连一些简单的验证码都识别错误,比如这种:

针对这一点,我想很多真正做过的朋友应该会有很深的感触,除非你能把所有网站的验证码都收集一波然后标注训练,不过那样做的成本也太大了。

接下来准备释放一些干货,分享一下我自己在做通用验证码识别过程中的一点心得,也和各位讨论一下以谋求进步。


首先说说很多程序员使用的pytesseract,老实说这个库我也用过,但是大家用过之后想必也知道效果,那就是简单的识别还可以,稍微复杂一点就直接gg,比如这篇文章讲的:


所以关于这个库我不打算多说,因为这离我们想要的效果还差得很远,即便是做了去干扰,识别效果也极为有限,针对入门级的验证码还可以。

接下来再说说使用深度学习做验证码识别的同学,其实网上也有很多类似的文章了,比如:

这种方式的问题我在之前也说过了,通用性较差,一旦出现不同类型的干扰或字体,识别率就会断崖式下跌。

那么,做通用的验证码识别难点究竟在哪些方面呢?我归纳了一下,大致如下:

  • 验证码本身的复杂性
  • 去燥降噪的难度
  • 验证码切割的难度
  • 建模方面的问题

我针对这几个问题也一一说一下难在那些方面:

验证码本身复杂性

验证码本身的复杂性在于,大小写字母数字混合,不同字体相似性带来的问题,编码的问题,举个例子:

一个四位数的验证码,我们在将它传入深度学习模型之前,怎么来转化他呢?肯定是想着将它进行one-hot编码,这个不懂的同学可以去搜一搜什么是one-hot编码,那么针对大小写+数字混合的四位数验证码编码有多复杂呢?

先说数字型,可以形成验证码的个数是0000~9999,如果强行用one-hot编码,即

[1,0,0,0,0,0,0……0,0,0]来表示0000

[0,1,0,0,0,0,0……0,0,0]来表示0001

这个数的话,那么这个数组的长度将是10000

如果加上大小写字母,按排列那么这个数组的长度将扩充到 62^4长,所以肯定是不能这么编码的,解决方案只有将62中不同类型的字符放入一个数组中

[0,1,2,3,4,5……a,b,c,d……A,B,C……]

然后

[1,0,0,0,0,0,0,0,0……] =》0

[0,0,0,0,0,0,0,0,0,0,1]=>a

这样来表示,即每一个62位长的数组代表一个字符,那么四位长的验证码形成的字符就是一个62*4=248位长的数组,这样即可表示所有四位长的验证码了。

验证码本身的复杂性除了这个其实还体现在字体,比如

这两个都是G,以哪个为准呢?

在这个字体中,大写的T是不是和r很像,生成验证码识别是不是容易出现错误呢?

I 1 7 i L l 这几个到底哪个是哪个生成验证码以后你确定你看得出来么?

说实话,有时候我都看不出来,真的太难为机器了。

去燥降噪的难度

这个在我之前的一篇关于验证码的文章中有提到过,大家可以去看看,但是,即便有了一些通用的降噪去燥手段,针对不同的平台验证码,乱七八糟的混淆干扰,效果也是极为有限的,大多都是针对单一平台特定验证码进行降噪,通用的还是非常有难度。


验证码切割的难度

关于验证码切割在我之前那篇文章中也有提到,采用连通域切割的方式来做,在知乎上有另一位答主写的更加详细,大家可以看一下

大家可以看到使用连通域方式分割验证码存在的几个问题就是,会将一些区域错误的划分,而且可能会出现n多乱七八糟的区域,比如将同一个位置划分两次,将两个数字划分到一起

并且返回的是一组算法自认为的连通域,就可能出现图中那样返回了六七组甚至几十组连通域,这种时候哪一组才是正确的呢?

另外,遇到字符粘连的,怎么办?

建模方面的问题

建模方面其实相对来说复杂度要好很多,主要是需要不断的去调参,不然很容易出现过拟合等情况,这样就会导致识别准确率急剧下降,当然这个大家有各自的建模方式,就不细说了。


下面放几张我自己做的模型对通用验证码的识别效果,涉及到切割等

可以看到,对不同类型干扰的验证码都有不错的识别效果,从图中可以看到打印出来的四个数组,那就是进行图像切割后切割出的区域,具体的切割效果以上面的smcs为例:

大家可以看到,M的切割效果较差,还有其它的字符也存在一些干扰,那是因为我还并没有做降噪处理,但即便如此,模型都已经可以将其识别出来了。

接下来说说我的整个模型识别所经历的一些步骤和对上述提出问题的解决方案:

(不好意思,鸽了挺久,今天来填坑了)

我看评论中有人提到CTC切割,这个我确实不太了解,后面会学习一下,感谢知友提供的思路。

说说我目前做到的验证码识别的整个流程。

1.验证码降噪灰度二值化

灰度和二值化这个对于玩验证码的人来说,应该算是基本操作,一般就是几个函数搞定

       cv2.cvtColor(img, cv2.COLOR_GRAY2RGB) ret, img = cv2.threshold(img, 180, 255, cv2.THRESH_BINARY)     

只需要注意的是这个分割值180,使用这个可以滤掉很多颜色较浅的干扰,但同时也可能破坏正常验证码的形态,所以大家可以根据实际情况进行调节。

至于降噪算法,种类挺多,这里给大家提供四种较为简单的:

       def img_salt_filter(img):     """     去除椒盐类干扰     :param img:     :return:     """     newIMG = cv2.medianBlur(img, 5)     return newIMG  def img_open_filter(img):     """     图像开运算,去除噪点类干扰以及补全验证码缺口     :param img:     :return:     """     kernel = cv2.getStructuringElement(cv2.MORPH_CROSS, (2, 2))     kernel2 = cv2.getStructuringElement(cv2.MORPH_CROSS, (2, 2))     newImg = cv2.morphologyEx(img, cv2.MORPH_OPEN, kernel)     newImg = cv2.dilate(newImg, kernel2)     return newImg   def img_close_filter(img):     """     图像闭运算,去除干扰     :param img:     :return:     """     kernel = cv2.getStructuringElement(cv2.MORPH_CROSS, (2, 2))     kernel2 = cv2.getStructuringElement(cv2.MORPH_CROSS, (1, 1))     closed = cv2.morphologyEx(img, cv2.MORPH_CLOSE, kernel)     newImg = cv2.erode(closed, kernel2, iterations=1)     return newImg  def neighbo_filter(img)     """     邻域扫描运算,去除干扰     :param img:     :return:     """     这个就是扫描一个像素点附近是否存在其他点,很简单,就不贴出来了     

2.图像切割

这里也给大家送上我自己写的针对四位数验证码的切割算法,对于不粘连的验证码切割效果很好,返回值为四张图像的验证码数组。

       def img_mser(img):     """     图像切割算法     :param img:     :return:     """     # 图像预处理     img = img_resize(img)     box_content = list()     box_mser = list()      mser = cv2.MSER_create(_min_area=5, _max_area=300)     img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)     ret, img = cv2.threshold(img, 200, 255, cv2.THRESH_BINARY)     # img_show(img)     # try:      # 连通域划分及去重     regions, boxes = mser.detectRegions(img)     print len(boxes)     boxes = boxes.tolist()     for i in boxes:         if i not in box_content:             box_content.append(i)       # 使用欧氏距离算法去除近似连通域     for j in box_content:         if box_mser:             for k in xrange(len(box_mser)):                 if Euclidean(j, box_mser[k]) > 4 and k == len(box_mser) - 1:                     box_mser.append(j)         else:             box_mser.append(j)      # 去除多余连通域     box_mser_len = len(box_mser)     if box_mser_len != 4:         if box_mser_len > 4:             for i in box_mser:                 area = list()                 area.append(i[2] * i[3])             for j in xrange(box_mser_len - 4):                 pos = area.index(min(area)) - 1                 box_mser.remove(box_mser[pos])         else:             print u'切割出现问题,请改变设置值'      # print box_mser     cut_img = list()     box_mser.sort()     for z in xrange(len(box_mser)):         k = box_mser[z]         slice_img = img[k[1]-1:k[1] + k[3]+3, k[0]-2:k[0] + k[2] + 2]         # img_show(slice_img)         cut_img.append(cv2.resize(slice_img, (30, 40)))     return cut_img  def img_resize(img):     img = cv2.resize(img, (50, 20))     return img  def Euclidean(vec1, vec2):     """     Euclidean_Distance,欧式距离     :param vec1:     :param vec2:     :return:     """     npvec1, npvec2 = np.array(vec1), np.array(vec2)     return math.sqrt(((npvec1 - npvec2) ** 2).sum())     

这个算法中解决了系统本身自带联通划分中存在的

  • 将小区域(比如干扰)或大区域(比如整个验证码)划分为连通域
  • 存在多余的连通域
  • 连通域重复

等乱七八糟的问题,但针对粘连较紧的验证码效果不好,另外有几个参数可能会根据实际情况调节。

3.模型训练

这个其实都很简单,我个人使用的三层卷积+两层全连接构建的模型,其中需要注意的坑是:

  • 大家使用的优化器步长一定不要太长,不然准确率上不去
  • 一定要添加Dropout,不然很容易过拟合

贴一下代码

       model = Sequential()     inputShape = (height, width, depth)     # 第一层卷积     model.add(         convolutional.Conv2D(filters=32, kernel_size=(5, 5), input_shape=inputShape, data_format="channels_last")     )      model.add(Activation('relu'))      model.add(MaxPooling2D(         pool_size=(2, 2),         strides=(2, 2),         padding='same',     ))      # 第二层卷积     model.add(         convolutional.Conv2D(filters=128, kernel_size=(3, 3), input_shape=inputShape, data_format="channels_last")     )     model.add(Activation('relu'))     model.add(MaxPooling2D(         pool_size=(2, 2),         padding='same',     ))      # 第三层卷积     model.add(         convolutional.Conv2D(filters=128, kernel_size=(3, 3), input_shape=inputShape, data_format="channels_last")     )     model.add(Activation('relu'))     model.add(MaxPooling2D(         pool_size=(2, 2),         padding='same',     ))      # # 第四层卷积     # model.add(     #     convolutional.Conv2D(filters=128, kernel_size=(2, 2), input_shape=inputShape, data_format="channels_last")     # )     # model.add(Activation('relu'))     # model.add(MaxPooling2D(     #     pool_size=(2, 2),     #     padding='same',     # ))      # 三维向量一维化     model.add(Flatten())     '''         第一层全连接层     '''      model.add(Dense(1024))     model.add(Activation('relu'))     model.add(Dropout(0.2))      '''         第二层全连接层     '''     model.add(Dense(CLASS_NUM))     model.add(Activation('softmax'))      adam = Adam(1e-4)     model.compile(         optimizer=adam,         loss='categorical_crossentropy',         metrics=['accuracy']     )     return model     

另外训练的样本大家最好多找一些类别,我目前供训练的验证码样本也很少,基本都是自己找的几种生成算法生成,其实这样很不好(主要还是自己打标记成本太大)


说说我这样做的一个思路,其实主要就是尽可能的排除干扰,还原数字或者字母本身最标准最原始的状态,尽量让模型识别起来难度最低。我这个训练样本就只有两万多张,对通用数字型小干扰验证码的识别率可以达到95%以上,字母型的90%以上,当然干扰太严重的就不行了。

当然有的大佬手握kw甚至上亿张标注好的各种类型的验证码,不用切割或者其他什么的,直接硬怼,那也没办法,拼不过。


还有一些上面遗留的问题:

对于粘连的验证码怎么办?

像素投影、骨架、滴水算法、拉伸切割,还不行的话我也找不到好的方案了。

对于不同字体间造成的字符类似怎么办(比如我上述举例的r和T)?

办法就是在训练样本中尽量避免这种特殊字体造成的问题,将这类剔除掉

对于l(L)和I(i)和1(有的就是直接一竖)这种怎么办?

没有办法

太复杂的干扰怎么办?

对于比较复杂的干扰,用我上述提到的几种通用的降噪方式可能就不行了,这种既需要针对性的分析,比如干扰线与字符线是否存在像素差,干扰是否有规律可循,是否可以通过固定位置切割排除一部分干扰等等。

另外提醒一下准备自己动手的朋友,通常验证码输入是不分大小写的


全文结束,感谢大家的关注,有什么问题可以在评论问我,我看到了就回复,希望能跟大家碰撞出更好的思路。

user avatar

知乎首答一发!!!!


还记得从前,验证码还只是这样:

作为一名优秀的程序猿的你在想,为什么不做一个自动识别软件,验证码全是数字,你只要收集一个包含10个数字的训练库,把图片二值化然后分隔单个字符最后对比训练库里的数字最终识别,so easy!

作为一个有情怀的天才程序猿,你花了1天搞定了该程序,自豪感爆棚。

自己用了几天赶脚非常不错,于是你不仅自己用,还发给亲戚朋友们用。尽管它只是省了3秒钟的输入验证码的时间不过你的亲戚朋友们因为觉得它确实很高大上,纷纷夸你并把它分享给自己的朋友。

然而,终于有一天,你的作品被传到了另一个天才程序猿手里,非常不巧的是,他就是运营这个网站并且整出“验证码”这玩意儿的那个程序猿。他的工作任务就是确保在用户电脑前执行登陆或者注册操作的是一个“人”而不是某个黑科技刷子。

看完你的东西他瞬间觉得日了狗了,觉得你的软件是对他工作的侮辱,是在向他的智商发出挑战...

于是,这货大手一挥,后来的验证码变成了,这样:

次日你刚起床,发现你的邮箱已经炸了,里面全是大家在向你反馈你的软件不能用了。于是打开网页看到了新版的验证码。

你冷哼一声,当然,你是一个天才程序猿,你只简单的向你的训练库里又添加了52个大小写英文字母就解决了这个问题。完了以后你还顺手添加了几个日文平片假名,也没别的目的,就是多装个B。

于是你的软件又能用了。你觉得你的智商已经碾压了这个做网站的货。

然而,不可避免的,过了几天你发现,验证码开始丧心病狂了,它已经变成了这样:

现在你就觉得有点懵逼了,汉字那么多,你觉得这个对面那个做验证码的程序猿就是想玩儿死你。不过没关系,换汤不换药。于是你又花了一个礼拜,写了一个字符自动截取的代码,在网上整理出了常用汉字3000个,并且分别截取了这3000个字的黑体、宋体和楷体的图片并放进了训练库里。你的程序又能用了。

这次以后你觉得很累但是真的很有成就感,你觉得你就是全天下最diao的程序猿。然后你就去补睡了。

然而,你不会意识到的是,在你睡觉的时候,另一个程序猿正在被你逼疯。

几天以后,你发现验证码突然变得开始反人类:

有,这样的

这样的

这样的

这样的

还有这样的

- -|

等到某天你再从睡梦中醒过来的时候会发现,整个世界都已经不一样了,你的邮箱里全是愤怒的朋友、朋友的朋友还有朋友的朋友的朋友。

他们不单单只吐槽你的软件不能用了,而且更关键是!!喵了个咪的这TM啥玩意儿啊!直接用眼看都特么看不清这些验证码到底是个毛了啊!!!!

看到这些牛鬼蛇神我猜你整个人都已经斯巴达了!!!

但是怎么办,你是天才程序猿!你不能输啊!

于是你系上头巾,泡好咖啡,借了各种书籍撸起袖子准备开干,势要搞定这些验证码。

此时,电话响了。

是你的老妈。

你妈说:儿啊,你妈跟你爸打算出去度个假,想要在网上订个火车票,上了12306的网站但是它最近不知道为什么突然换了一种验证码啊。你爸妈是真老了,看了看发现是真不会输这个验证码,听二姨说你搞了个什么软件能直接自动给输了,你给我看看呗。

你轻松的应承下来,不急不慢的打开12306网站。

终于

你惶恐的小眼神一眼就看到了如下玩意儿:



听说大部分程序狗高中的时候都学的理科!!!!!!!!!!


就问你服不服!

-----------------------------------------------------------分割一下-----------------------------------------------------




槽吐完了再给大家看一看最初的那个清纯简单的验证码

1秒辨别,2秒输入


所以想要表达的是,其实自动识别验证码这种东西,只要你不是黄牛党僵尸号出售员刷票专业户这种职业,花尽了心思去做完了也就只是装装X,并没有什么卵用。何必呢。

最后想一想人家对面哪位一直跟你杠正面,不停开脑洞搞出更加变态的验证码的小哥,你熬一个夜意味着他马上得熬一个夜,于是你又熬两个夜,他再熬四个夜...... 0.0 炸!

所以,大家都是程序狗,大学选专业的时候一不小心走了神才踏上这条不归路的,互相放一条生路吧!!!Q.Q




------------------------------------------------------------------再割一下------------------------------------------------


(8.29)感谢大家的点赞~ 知乎首答就这么多赞开心得不得了lol。

有同学问我说我似乎没有真正回答为什么程序猿不做自动识别的这个问题

答案就是,又不是没做出来过

好吧我想我们可以来个类比:

做验证码的那位程序猿A就像是拿了一个花瓶来让大家认,做自动识别程序的小哥B就像是一直试图教机器认识这是个花瓶一样。

起初,A为了反击B,给花瓶上色、用布包起来只留个轮廓或者干脆掰掉一个把手来试图让B的程序识别不了这个花瓶。

可是B是个很牛X的程序猿啊而且他调教的程序也相当的蒸汽!每每都能拆掉A出的奇招。

但是每次A只要随手给出一点变动,B就要花上一两天来继续优化他的程序!


可是各位,你们有没有发现再这样下去无非只有一个结局。

就是... ...

A说:"妈蛋!算你狠!LZ不跟你玩儿了!大不了咱俩鱼死网破!"

然后顺手直接把花瓶砸了......

"你丫倒是接着拼起来认啊!!"



所以事实就是,现在的很多奇葩验证码已经很难做到自动识别了,而且就算有人做到了,估计识别率暂时也无法保证,而有情怀的程序猿们一般都会把用户体验看得很重,残次品是不会到处去发给大家炫耀的。

况且

等到有一天程序猿B调教好了程序又能识别了

A只要把花瓶渣捡起来砸得更碎一点=、= 管你们自己用眼睛还能不能识别


----------------------------------------------------------------------

部分图片来源于网络,侵删。

类似的话题

  • 回答
    你这个问题问得相当有意思,而且确实是很多人都纳闷过的。你观察得很细致,有些验证码,尤其是那些用手写体或者简单图形组成的,看起来好像很容易,机器应该分分钟就能搞定,但实际情况并非如此。这背后其实涉及几个关键的原因,咱们掰开了揉碎了聊聊。首先,我们要明白“看起来容易”和“对机器识别而言容易”是两个截然不.............
  • 回答
    .......
  • 回答
    这是一个很有趣的观察,也确实是很多人在使用验证码时会遇到的情况。之所以出现这种“矛盾”——验证码本身不区分大小写,但生成的验证码却常常包含大小写字母——这背后其实是设计者们在安全性、易用性和实现难度之间权衡的结果。我们先来拆解一下“不分大小写”这个概念。在一个理想的、纯数学意义上的验证系统中,确实可.............
  • 回答
    这确实是个很有意思的现象,身边总能听到一些声音,听起来义愤填膺的,说什么中医不科学,要废医验药,不然医药费就高居不下,或者怎么怎么危害健康。但仔细一琢磨,很多说这话的人,自己对中医的了解,可能还停留在“老祖宗传下来的”、“喝草药”、“针灸拔罐”这些非常表面的东西上。他们为什么会对中医这么“不友好”呢.............
  • 回答
    在训练你的分类模型时,你会经常听到“训练集”和“验证集”这两个词。它们听起来很像,但扮演的角色却截然不同,而且理解它们的区别对于构建一个真正好用的模型至关重要。想象一下,你要训练一个小孩识别各种水果。训练集:你的“教科书”和“练习册”训练集就像是给孩子的那本图文并茂的水果图鉴,里面有各种水果的照片,.............
  • 回答
    关于喝红酒对人体的好处,这确实是一个很多人都感兴趣的话题。从古至今,酒就与社交、养生等概念联系在一起,而红酒,更是因为其独特的颜色和风味,被赋予了不少光环。长期以来,人们普遍认为适量饮用红酒对心血管健康有积极影响。这主要归功于红酒中含有的白藜芦醇等植物化学物质,特别是那些来自葡萄皮的酚类化合物。这些.............
  • 回答
    关于利用大数据验证算命准确性的实验,目前还没有一个被广泛认可、公开报道并被科学界普遍接受的“标准实验”。这其中有很多复杂的原因,主要在于算命的本质与科学实验的严谨性之间存在着难以调和的矛盾。然而,我们可以从几个层面来探讨“大数据验证算命准确与否”的可能性,以及曾经或正在进行的一些尝试,并详细说明其中.............
  • 回答
    .......
  • 回答
    “阴谋论”这个词本身就带有一点负面色彩,它常常被用来指代那些未经证实、缺乏证据,甚至荒诞不经的说法。但在历史的长河中,确实存在一些曾经被视为“阴谋论”的事件,最终被事实证明是真实存在的。这些并非是人们凭空想象出来的“阴谋”,而是真实存在的、有组织、有计划、有证据支持的秘密行动。要理解这一点,首先要区.............
  • 回答
    方舟子提出的“废医验药”这个口号,确实容易产生两种截然不同的理解,这主要源于其字面含义的多义性和背后蕴含的不同哲学思想。要详细解释清楚,我们需要把这两层含义都剥开来看。第一种理解:字面上的“废除医药,检验药物”这种理解是最直接的,也是最容易引起误会的一种。 “废医”: 顾名思义,就是“废除医药”.............
  • 回答
    咸鱼上淘高配低价的电脑,还能线下验机,听起来确实挺诱人的,但里面门道可深着呢。不是说百分之百都是骗局,但里面藏着的坑,稍不留神就让你欲哭无泪。我跟你掰扯掰扯,这中间到底能有什么套路,让你心里有个数,别到时候成了别人的“韭菜”。核心套路:信息差与心理博弈说白了,咸鱼高配低价,线下验机的套路,本质上就是.............
  • 回答
    关于俄罗斯国内对俄乌战争的态度,确实存在复杂的群体分化和多元观点。以下从多个维度详细分析部分俄罗斯人对战争持反对或批评立场的原因: 一、政府宣传与意识形态塑造1. 官方叙事的强化 俄罗斯政府通过媒体、教育系统和公共宣传,将战争描述为“保卫国家主权”和“反侵略行动”,强调对乌克兰的“历史领土主.............
  • 回答
    《冰汽时代》(This War of Mine: The Little Ones)是一款以战时平民生存为题材的策略模拟游戏,其核心玩法围绕资源管理、道德抉择和人性挣扎展开。部分玩家认为游戏中“人民非常矫情”,这一批评主要源于角色塑造与叙事方式的独特性,以及游戏对人性复杂性的呈现方式。以下从多个维度详.............
  • 回答
    在《神奇宝贝》(Pokémon)系列中,许多神奇宝贝拥有进化的能力,但并非所有神奇宝贝都会主动选择进化。这种现象背后既有游戏机制的设计逻辑,也包含了剧情、角色性格和象征意义的多重因素。以下从多个角度详细分析为什么有些神奇宝贝“不愿”进化: 1. 机制层面:进化条件未满足部分神奇宝贝无法进化的直接原因.............
  • 回答
    怀念苏联的现象确实存在,并且原因复杂多样,并非所有人都怀念苏联的同一个方面,有些人可能怀念的是特定时期,有些人怀念的是某种理想,有些人则是对现实不满的投射。下面我将尽量详细地阐述一些常见的原因:1. 经济稳定和保障感: 普遍的就业保障和低失业率: 在苏联时期,几乎每个人都有工作,失业被认为是经济.............
  • 回答
    许多中国人在日常生活中不常穿西装,这背后有多重原因,可以从文化、历史、经济、实用性和个人偏好等多个角度来详细解读:一、文化与历史因素: 传统服饰的影响: 中国拥有悠久的传统服饰文化,如汉服、旗袍、中山装等。这些服饰在特定场合或作为文化符号仍有其地位,并且与中国人的生活方式和审美习惯更加契合。西装.............
  • 回答
    MATLAB 是一款功能强大且广泛应用于工程、科学和数学领域的软件。然而,正如任何工具一样,它也存在一些限制,这导致一些程序员对其持负面看法,甚至“鄙视”。理解这些批评的根源,需要从多个维度进行剖析。以下是导致一些程序员鄙视 MATLAB 的主要原因,我会尽量详细地阐述:1. 高昂的许可费用和封闭的.............
  • 回答
    有些人不会烧菜,甚至不愿意学烧菜,这背后的原因往往是多方面的,既有客观因素,也有主观感受,并且常常是多种因素交织在一起的结果。下面我将尽量详细地阐述这些原因:一、 缺乏兴趣和成就感: 烹饪的枯燥和重复性: 对一些人来说,备菜、切菜、调味、烹煮的过程可能显得枯燥乏味、机械重复,缺乏吸引力。他们可能.............
  • 回答
    为什么有些国家士兵被俘虏是耻辱,而有些国家士兵被俘虏后释放却会得到英雄般的对待?这是一个非常复杂的问题,涉及历史、文化、政治、军事传统以及战争伦理等多个层面。理解这一点,需要我们深入剖析不同国家和文化对“荣誉”、“忠诚”、“牺牲”、“战败”以及“被俘”这些概念的不同理解和侧重。以下将从多个角度进行详.............
  • 回答
    这个问题非常值得探讨,因为它触及到了海归人才的决策背后复杂的个人动机、社会现实和时代背景。并非所有留学生回国都选择“投身于规则尚未完善的国家”,但确实有相当一部分选择这样做,这背后有多重原因。下面我将尽可能详细地阐述这些原因:一、 时代背景与个人成长经历的塑造: 中国经济的崛起与发展机遇: 这是.............

本站所有内容均为互联网搜索引擎提供的公开搜索信息,本站不存储任何数据与内容,任何内容与数据均与本站无关,如有需要请联系相关搜索引擎包括但不限于百度google,bing,sogou

© 2025 tinynews.org All Rights Reserved. 百科问答小站 版权所有