ChatGPT 简介 - GPT 系列的发展和人工智能领域的探索
背景
在去年十二月到现如今,也已经过去了几个月,可我们的ChatGPT仍然热度不减,甚至带动了一批行业发展,有些人更是利用它赚得盆满钵满,但作者却一直对其不甚了解,注册账号要国外手机号码,有成本,一些人做的小程序也是很容易挂掉,都让我不是很想去使用它。
直到大数据也开始对作者本人下手,刷到的视频,看到的文章都是说用这个ChatGPT如何好,还已经降低门槛,国内也出现了许多这方面技术的公开,总之就是突出一个使用简单就是了,所以,本周末(3.11-3.12),作者利用空余时间进行探索,去看看到底是否降低了门槛?
探索结果
使用前提:你得有个神秘上网方法,让我们可以正常的访问google之类的(作者目前无法避免这一步骤)。
作者首先看了我们站内的油壶大佬文章(OpenAI 推出超神 ChatGPT 注册攻略来了 - 掘金 (juejin.cn))以及下面的评论,基本得出注册还是有一定门槛的,至少去年就如此了,首先要用国外手机号码接受短信验证,就得花money,虽然当时很少,但评论区很多都是失败了,而且注册成功也很容易ip原因访问被拒,反正结论就是比较麻烦,后面又看了另一篇人气较高的文章(ChatGPT保姆级教程,一分钟学会使用ChatGPT! - 掘金 (juejin.cn)),其实和油壶的文章差不多,不过更新一些,最终作者也忍不住去试着注册,作者试着用了一刀,然后绷不住了,网站上说是还有20分钟的那种,但实际最低要租4小时了,这就等于我的一刀很多国家都不够用的(3月16日更新:网站回到了20分钟的便宜模式):
于是作者狠心再下一刀,然后成功注册了openAI,这才开始正式使用ChatGPT,而且也可以去用它的API,好像之前看的文章说有18刀免费额度,可是现在我上面看是只有5刀的免费额度了:
而我们如果想使用它的api去接入我们开发中,根据openAI的官方报价,是每1000次token收0.002刀,据各大平台文章的说法,大概是750单词就1000token(官方提供了检查工具OpenAI API):
而我们的ChatGPT就是基于gpt系列的gpt-3.5实现的。
当然,如果是想体验更全更完整的chatGPT对话服务,就要每月花上20刀去享用它的plus服务(还是不了解有什么用,我感觉普通的也挺好的)。
使用体验
目前来说,普通服务已经能够用来满足我们尝鲜了,感觉可以回答但内容不是很全,至少我提问的东西它写了,但没完全写完:
我们可以再叫它接着写,它就能写完:
还给我们提供了相关的说明解释,非常贴心:
下面贴上它给的代码:
class FireworksAnimationView(context: Context?, attrs: AttributeSet?) : View(context, attrs) {
private val fireworksList = mutableListOf<Firework>()
private val paint = Paint(Paint.ANTI_ALIAS_FLAG)
private val random = Random()
private var animationDuration = 3000L
private var timer: CountDownTimer? = null
// Firework attributes
private var fireworkCount = 6
private var fireworkSize = 50f
private var fireworkColor = Color.WHITE
private var fireworkRadius = 10f
// Explosion attributes
private var explosionCount = 30
private var explosionSize = 5f
private var explosionColor = Color.YELLOW
init {
paint.color = fireworkColor
paint.style = Paint.Style.FILL
}
fun start() {
timer?.cancel()
timer = object : CountDownTimer(animationDuration, 1000 / 60) {
override fun onTick(millisUntilFinished: Long) {
fireworksList.forEach { firework ->
firework.update()
}
invalidate()
}
override fun onFinish() {
start()
}
}.start()
}
override fun onDraw(canvas: Canvas?) {
super.onDraw(canvas)
fireworksList.forEach { firework ->
paint.color = firework.color
canvas?.drawCircle(firework.x, firework.y, fireworkSize, paint)
firework.explosions.forEach { explosion ->
paint.color = explosion.color
canvas?.drawCircle(explosion.x, explosion.y, explosionSize, paint)
}
}
}
override fun onTouchEvent(event: MotionEvent?): Boolean {
if (event?.action == MotionEvent.ACTION_DOWN) {
createFirework(event.x, event.y)
}
return true
}
private fun createFirework(x: Float, y: Float) {
val firework = Firework(x, y, fireworkColor)
firework.explosions.addAll(createExplosions(firework))
fireworksList.add(firework)
}
private fun createExplosions(firework: Firework): List<Explosion> {
val explosions = mutableListOf<Explosion>()
for (i in 1..explosionCount) {
val explosionX = random.nextInt((fireworkSize * 2).toInt()) - fireworkSize + firework.x
val explosionY = random.nextInt((fireworkSize * 2).toInt()) - fireworkSize + firework.y
val explosion = Explosion(explosionX, explosionY, explosionColor)
explosions.add(explosion)
}
return explosions
}
inner class Firework(var x: Float, var y: Float, val color: Int) {
val explosions = mutableListOf<Explosion>()
private val speed = 10f
private var acceleration = 1.05f
fun update() {
y -= speed
speed *= acceleration
if (speed > 50f) {
explode()
fireworksList.remove(this)
}
}
private fun explode() {
for (explosion in explosions) {
explosion.start()
}
}
}
inner class Explosion(val startX: Float, val startY: Float, val color: Int) {
var x = startX
var y = startY
private var radius = fireworkRadius
private var alpha = 1f
private var duration
//接着写...
private var time = 0f
private var totalTime = 1.5f
fun start() {
ValueAnimator.ofFloat(0f, totalTime).apply {
duration = (totalTime * 1000).toLong()
addUpdateListener {
time = it.animatedValue as Float
// Update position
val fraction = time / totalTime
x = startX + (fireworkRadius * fraction * cos(fraction * 6.28f))
y = startY + (fireworkRadius * fraction * sin(fraction * 6.28f))
// Update alpha and radius
alpha = 1 - time / totalTime
radius = fireworkRadius * alpha
invalidate()
}
start()
}
}
fun isAlive(): Boolean {
return time < totalTime
}
}
给的解释:
这段代码创建了一个自定义的 View,名为 `FireworksAnimationView`,用于显示烟花动画效果。您可以在您的布局文件中添加该视图,并通过调用 `start()` 方法来开始动画。
该代码使用了面向对象的设计,定义了 `Firework` 和 `Explosion` 两个类来表示烟花和爆炸效果。在 `onTouchEvent` 方法中,当用户点击屏幕时,会创建一个新的 `Firework` 对象,并加入到 `fireworksList` 中。在 `onDraw` 方法中,会遍历 `fireworksList` 中的所有烟花,并依次绘制它们及其爆炸效果。
`Firework` 类表示一个烟花对象,它具有 `x`、`y` 坐标、颜色等属性,以及一个 `explosions` 列表,用于存储它的爆炸效果。它还定义了一个 `update` 方法,用于更新烟花的位置和速度,并在烟花的速度达到一定值时触发爆炸效果。爆炸效果由 `Explosion` 类来表示,它具有起始坐标、颜色、半径等属性,以及一个 `start` 方法,用于开始爆炸动画。
该代码使用了 `ValueAnimator` 类来实现爆炸动画效果。在 `Explosion` 类的 `start` 方法中,会创建一个 `ValueAnimator` 对象,并使用它来更新爆炸效果的位置、透明度和半径。在 `Explosion` 类的 `isAlive` 方法中,会检查当前爆炸效果是否还在播放中。
您可以根据自己的需要来调整烟花和爆炸效果的属性,例如烟花数量、大小、颜色、半径等等。同时,您也可以添加更多的属性和方法来扩展该代码,并实现更丰富的烟花动画效果。
当然,作者也没去验证这段代码是否可行,因为也是仅仅去体验一把,ai的能力。
GPT系列的发展
目前看到几个视频和文章的评论区,都说国内目前很多套壳ChatGPT其实都是使用免费的开源GPT-3的api服务的(未查证,但作者查到GPT-3并没有开源,可能国内有一些复现的比较好的),而我们现在说的很厉害的ChatGPT,其实是基于gpt-3.5的,更加强大,而且数据更全,虽然数据也就收录到2021,你如果问它2022的事情,它是不知道的,也有可能和你撒谎乱说。
而据资料显示,GPT-3指的是生成型预训练变换模型3(英语:Generative Pre-trained Transformer 3,简称GPT-3)是一个自回归语言模型,为有史以来参数最多的神经网路模型,已经在2020年就被微软获得研制它的openAI公司的独家授权,而我们的GPT3.5则是基于3微调出来的,而且因为训练方式不同,得出的模型不同,更为强大,最后是ChatGPT,则是基于GPT-3.5,但使用更完整的 人类反馈强化学习(RLHF) 去训练得到的,它能够准确理解我们问的问题,还能够将对话记住(比如作者之前的“接着说”)且按此调整内容,而其中的包括承认错误、纠正错处和拒绝不当要求等较为复杂的互动内容,更加符合人类道德要求的训练方式,以达到更接近真人的效果,这也是之前提到的模型GPT-3所没有的能力。
(资料来源:GPT-3是什么,GPT-3和GPT3.5、ChatGPT是什么关系? - 爱绿豆 (iludou.com))
而根据最新消息透露,3月9日德国微软CTO在一场AI活动中表示GPT-4将于下周发布,可以更好的服务人类,这也说明AI技术发展确实很猛。
人工智能领域的探知
人工智能发展已经很长时间了,它所涉及的领域很多很复杂,有的已经在我们身边,比如我自己就开发过关于智能识别模块的业务需求,不过我也仅仅是接入公司的智能识别库而已,也就是不知道内部的算法是什么。
除此以外,通过我的朋友我知道了他们人工智能在研究生阶段有着车辆重识别和行人重识别等等这些课题,查询资料后我发现都是利用各种算法(神经网络,深度学习,人在回路之类的)让AI更加强大,让它对相应的行业领域更为适应,也更为专精,而我们大多数开发或应用层面仍然只是去调用这个算法库去迭代数据,让操作更变得为简单,而更多的普通人只是去享受这些AI能力带来的服务升级。
但人工智能全是好的吗?在作者眼中也不尽然,因为他们确确实实让很多重复性工作或者随着它的进步,让我们很多简单性的工作不需要人参与了,这就造成了短暂性的很多人失业,因为长久来说,AI也带来了很多就业机会,但目前来说,产业的升级会淘汰一部分岗位,就如之前的工业化一样,那如何去适应这一阶段也是很多决策者需要考虑的,我们只能去面对它,相信就如人在回路离不开人这一条件,AI的发展肯定也是需要大量的劳动力去改善它的。
思考结束,再回归我们的正文,人工智能现如今已经是一个庞然大物,甚至在学术界据我的朋友所说,发表的论文甚至有饱和现象了,所以各行各业的人工智能也是五花八门,再加上近年来量子计算的发展加持与数据量足量的迭代,感觉不久的未来AI应该还会有一次大升级,是否会出现如《流浪地球》第二部中的moss也不无可能。
尾声
其实本来想着以chatGPT的api接入安卓app例子来结束的,但作者目前还没做好,加上作者最近也在品读阿西莫夫的银河帝国系列书籍,所以就发了一些感触。其实AI技术的大发展和我们从手工到机器再到大机器时代的发展都是差不多的,重要的是我们是否能够去面对它,或者换个思路去利用好它们,是否在面对真正强大起来的AI时能让它们去遵循着“机器人三大定律”(其实还有第零定律:机器人必须保护人类的整体利益不受伤害),而且是否这样的定律也有着不足,需要适应当下,这些想法可能在不久就真的要考虑起来了。
本文正在参加「金石计划」
上一篇: 永远不要沮丧。这东西需要声音
下一篇: 慰问(容嬷嬷)
推荐阅读
-
ChatGPT 简介 - GPT 系列的发展和人工智能领域的探索
-
InfoQ,谈谈百度开源高性能搜索引擎 Puck-Ben:Puck是团队长期研究和努力的成果,作为Puck的负责人,我对这个项目有着深深的热爱和执着,对我个人而言,它不仅仅是一个搜索引擎,而是代表着团队心血和智慧的结晶,它是我们对技术的追求,对创新的执着,也是我们对未来的期望和愿景,Puck的每一次升级和优化都记录着我们的成长和进步。这是我们对技术的追求,对创新的执着,也是我们对未来的期望和憧憬,帕克的每一次升级和优化都记录着我们的成长和进步。 我对帕克的未来充满期待。首先,我希望 Puck 能够在开发者社区得到广泛应用,同时得到社区的反馈,不断优化和改进。我期待看到更多的人参与到Puck的开发和使用中来,通过大家的共同努力,让Puck成为人工智能领域有影响力的工具。其次,我希望Puck能够不断创新和优化,保持技术领先地位,不仅要适应当前的技术需求,更要预测和引领未来的技术趋势。最后,我希望Puck能在更多的实际应用中实现自身价值,为人工智能在各行各业的应用提供有力支撑,推动科技发展。 访谈嘉宾简介: Ben,百度搜索内容技术部主任架构师,负责多模态内容理解、超大规模内容关系计算、内容处理与生成、模型优化等方向。 欢迎加入朋克技术交流群:913964818 本部门招聘ANN搜索工程师、模型优化工程师、分布式计算研发工程师等多个职位。欢迎勇于接受挑战、具有优秀分析和解决问题能力的人才加入我们。 招聘邮箱:tianyakun@baidu.com --END-- 推荐阅读
-
F#探险之旅(二):函数式编程(上)-函数式编程范式简介 F#主要支持三种编程范式:函数式编程(Functional Programming,FP)、命令式编程(Imperative Programming)和面向对象(Object-Oriented,OO)的编程。回顾它们的历史,FP是最早的一种范式,第一种FP语言是IPL,产生于1955年,大约在Fortran一年之前。第二种FP语言是Lisp,产生于1958,早于Cobol一年。Fortan和Cobol都是命令式编程语言,它们在科学和商业领域的迅速成功使得命令式编程在30多年的时间里独领风骚。而产生于1970年代的面向对象编程则不断成熟,至今已是最流行的编程范式。有道是“*代有语言出,各领风骚数十年”。 尽管强大的FP语言(SML,Ocaml,Haskell及Clean等)和类FP语言(APL和Lisp是现实世界中最成功的两个)在1950年代就不断发展,FP仍停留在学院派的“象牙塔”里;而命令式编程和面向对象编程则分别凭着在商业领域和企业级应用的需要占据领先。今天,FP的潜力终被认识——它是用来解决更复杂的问题的(当然更简单的问题也不在话下)。 纯粹的FP将程序看作是接受参数并返回值的函数的集合,它不允许有副作用(side effect,即改变了状态),使用递归而不是循环进行迭代。FP中的函数很像数学中的函数,它们都不改变程序的状态。举个简单的例子,一旦将一个值赋给一个标识符,它就不会改变了,函数不改变参数的值,返回值是全新的值。 FP的数学基础使得它很是优雅,FP的程序看起来往往简洁、漂亮。但它无状态和递归的天性使得它在处理很多通用的编程任务时没有其它的编程范式来得方便。但对F#来说这不是问题,它的优势之一就是融合了多种编程范式,允许开发人员按照需要采用最好的范式。 关于FP的更多内容建议阅读一下这篇文章:Why Functional Programming Matters(中文版)。F#中的函数式编程 从现在开始,我将对F#中FP相关的主要语言结构逐一进行介绍。标识符(Identifier) 在F#中,我们通过标识符给值(value)取名字,这样就可以在后面的程序中引用它。通过关键字let定义标识符,如: let x = 42 这看起来像命令式编程语言中的赋值语句,两者有着关键的不同。在纯粹的FP中,一旦值赋给了标识符就不能改变了,这也是把它称为标识符而非变量(variable)的原因。另外,在某些条件下,我们可以重定义标识符;在F#的命令式编程范式下,在某些条件下标识符的值是可以修改的。 标识符也可用于引用函数,在F#中函数本质上也是值。也就是说,F#中没有真正的函数名和参数名的概念,它们都是标识符。定义函数的方式与定义值是类似的,只是会有额外的标识符表示参数: let add x y = x + y 这里共有三个标识符,add表示函数名,x和y表示它的参数。关键字和保留字关键字是指语言中一些标记,它们被编译器保留作特殊之用。在F#中,不能用作标识符或类型的名称(后面会讨论“定义类型”)。它们是: abstract and as asr assert begin class default delegate do donedowncast downto elif else end exception extern false finally forfun function if in inherit inline interface internal land lazy letlor lsr lxor match member mod module mutable namespace new nullof open or override private public rec return sig static structthen to true try type upcast use val void when while with yield 保留字是指当前还不是关键字,但被F#保留做将来之用。可以用它们来定义标识符或类型名称,但编译器会报告一个警告。如果你在意程序与未来版本编译器的兼容性,最好不要使用。它们是: atomic break checked component const constraint constructor continue eager event external fixed functor global include method mixinobject parallel process protected pure sealed trait virtual volatile 文字值(Literals) 文字值表示常数值,在构建计算代码块时很有用,F#提供了丰富的文字值集。与C#类似,这些文字值包括了常见的字符串、字符、布尔值、整型数、浮点数等,在此不再赘述,详细信息请查看F#手册。 与C#一样,F#中的字符串常量表示也有两种方式。一是常规字符串(regular string),其中可包含转义字符;二是逐字字符串(verbatim string),其中的(")被看作是常规的字符,而两个双引号作为双引号的转义表示。下面这个简单的例子演示了常见的文字常量表示: let message = "Hello World"r"n!" // 常规字符串let dir = @"C:"FS"FP" // 逐字字符串let bytes = "bytes"B // byte 数组let xA = 0xFFy // sbyte, 16进制表示let xB = 0o777un // unsigned native-sized integer,8进制表示let print x = printfn "%A" xlet main = print message; print dir; print bytes; print xA; print xB; main Printf函数通过F#的反射机制和.NET的ToString方法来解析“%A”模式,适用于任何类型的值,也可以通过F#中的print_any和print_to_string函数来完成类似的功能。值和函数(Values and Functions) 在F#中函数也是值,F#处理它们的语法也是类似的。 let n = 10let add a b = a + blet addFour = add 4let result = addFour n printfn "result = %i" result 可以看到定义值n和函数add的语法很类似,只不过add还有两个参数。对于add来说a + b的值自动作为其返回值,也就是说在F#中我们不需要显式地为函数定义返回值。对于函数addFour来说,它定义在add的基础上,它只向add传递了一个参数,这样对于不同的参数addFour将返回不同的值。考虑数学中的函数概念,F(x, y) = x + y,G(y) = F(4, y),实际上G(y) = 4 + y,G也是一个函数,它接收一个参数,这个地方是不是很类似?这种只向函数传递部分参数的特性称为函数的柯里化(curried function)。 当然对某些函数来说,传递部分参数是无意义的,此时需要强制提供所有参数,可是将参数括起来,将它们转换为元组(tuple)。下面的例子将不能编译通过: let sub(a, b) = a - blet subFour = sub 4 必须为sub提供两个参数,如sub(4, 5),这样就很像C#中的方法调用了。 对于这两种方式来说,前者具有更高的灵活性,一般可优先考虑。 如果函数的计算过程中需要定义一些中间值,我们应当将这些行进行缩进: let halfWay a b = let dif = b - a let mid = dif / 2 mid + a 需要注意的是,缩进时要用空格而不是Tab,如果你不想每次都按几次空格键,可以在VS中设置,将Tab字符自动转换为空格;虽然缩进的字符数没有限制,但一般建议用4个空格。而且此时一定要用在文件开头添加#light指令。作用域(Scope)作用域是编程语言中的一个重要的概念,它表示在何处可以访问(使用)一个标识符或类型。所有标识符,不管是函数还是值,其作用域都从其声明处开始,结束自其所处的代码块。对于一个处于最顶层的标识符而言,一旦为其赋值,它的值就不能修改或重定义了。标识符在定义之后才能使用,这意味着在定义过程中不能使用自身的值。 let defineMessage = let message = "Help me" print_endline message // error 对于在函数内部定义的标识符,一般而言,它们的作用域会到函数的结束处。 但可使用let关键字重定义它们,有时这会很有用,对于某些函数来说,计算过程涉及多个中间值,因为值是不可修改的,所以我们就需要定义多个标识符,这就要求我们去维护这些标识符的名称,其实是没必要的,这时可以使用重定义标识符。但这并不同于可以修改标识符的值。你甚至可以修改标识符的类型,但F#仍能确保类型安全。所谓类型安全,其基本意义是F#会避免对值的错误操作,比如我们不能像对待字符串那样对待整数。这个跟C#也是类似的。 let changeType = let x = 1 let x = "change me" let x = x + 1 print_string x 在本例的函数中,第一行和第二行都没问题,第三行就有问题了,在重定义x的时候,赋给它的值是x + 1,而x是字符串,与1相加在F#中是非法的。 另外,如果在嵌套函数中重定义标识符就更有趣了。 let printMessages = let message = "fun value" printfn "%s" message; let innerFun = let message = "inner fun value" printfn "%s" message innerFun printfn "%s" message printMessages 打印结果: fun value inner fun valuefun value 最后一次不是inner fun value,因为在innerFun仅仅将值重新绑定而不是赋值,其有效范围仅仅在innerFun内部。递归(Recursion)递归是编程中的一个极为重要的概念,它表示函数通过自身进行定义,亦即在定义处调用自身。在FP中常用于表达命令式编程的循环。很多人认为使用递归表示的算法要比循环更易理解。 使用rec关键字进行递归函数的定义。看下面的计算阶乘的函数: let rec factorial x = match x with | x when x < 0 -> failwith "value must be greater than or equal to 0" | 0 -> 1 | x -> x * factorial(x - 1) 这里使用了模式匹配(F#的一个很棒的特性),其C#版本为: public static long Factorial(int n) { if (n < 0) { throw new ArgumentOutOfRangeException("value must be greater than or equal to 0"); } if (n == 0) { return 1; } return n * Factorial (n - 1); } 递归在解决阶乘、Fibonacci数列这样的问题时尤为适合。但使用的时候要当心,可能会写出不能终止的递归。匿名函数(Anonymous Function) 定义函数的时候F#提供了第二种方式:使用关键字fun。有时我们没必要给函数起名,这种函数就是所谓的匿名函数,有时称为lambda函数,这也是C#3.0的一个新特性。比如有的函数仅仅作为一个参数传给另一个函数,通常就不需要起名。在后面的“列表”一节中你会看到这样的例子。除了fun,我们还可以使用function关键字定义匿名函数,它们的区别在于后者可以使用模式匹配(本文后面将做介绍)特性。看下面的例子: let x = (fun x y -> x + y) 1 2let x1 = (function x -> function y -> x + y) 1 2let x2 = (function (x, y) -> x + y) (1, 2) 我们可优先考虑fun,因为它更为紧凑,在F#类库中你能看到很多这样的例子。 注意:本文中的代码均在F# 1.9.4.17版本下编写,在F# CTP 1.9.6.0版本下可能不能通过编译。 F#系列随笔索引页面