git log 如果不带任何参数,它会列出所有历史记录,最近的排在最上方,显示提交对象的哈希值,作者、提交日期、和提交说明。如果记录过多,则按Page Up、Page Down、↓、↑来控制显示;按q退出历史记录列表。
git log -n 如果不想向上面那样全部显示,可以选择显示前N条。
git log –stat -n 显示简要的增改行数统计,每次提交文件的变更统计,-n 同上,前n条,可省略。
$ git log --stat -2
commit d0b9a20fac8abc7517c5a04c0fbb1d488f309bf5
Author: BeginMan <pythonsuper@gmail.com>
Date: Sat Mar 1 23:26:43 2014 +0800
ok -----commit时提交的说明
_posts/2014-02-27-Customizing-Git.md | 5 +++++
file changed, 5 insertions(+)
commit 8c186cd71492b7a3eae6df7de880b99efa0f87cf
Author: BeginMan <pythonsuper@gmail.com>
Date: Sat Mar 1 23:26:10 2014 +0800
mi
_posts/2014-02-27-Customizing-Git.md | 56 +++++++++++++++++++++++++++++++++++-
file changed, 55 insertions(+), 1 deletion(-)
每个提交都列出了修改过的文件,以及其中添加和移除的行数,并在最后列出所有增减行数小计,比如上面的有5行做了更新。
指定日期、关键字、作者
如两天前的提交历史: git log --since=2.days
如指定作者为”BeginMan”的所有提交: $ git log --author=BeginMan
如指定关键字为“init”的所有提交:$ git log --grep=init
如指定提交者为”Jack”的所有提交:$ git log --committer=Jack
注意作者与提交者的关系:作者是程序的修改者,提交者是代码提交人.
如指定2天前,作者为BeginMan
的提交含有关键字init
的前2条记录:
git log --since=2.days --author=BeginMan --grep=init -2
查看某次commit做了哪些修改
git log #查看commit的历史
git show <commit-hash-id> #查看某次commit的修改内容
官网宣传了3个特性:
个人认为 lottie 最大的优势就是可以将设计师设计的动图原原本本的在页面上展现出来,完美还原了动画的精细度,并且对动画拥有足够的控制能力。目前所有使用 gif 或 apng 的场景应该都可以使用 lottie,当然 lottie 不局限与此场景。
本文主要从设计师视角和开发者视角讲述 lottie-web 的原理和使用以及 lottie 在 weex/rax 中的使用。
在 AE 中为 lottie 创作动画,你需要以下准备
安装 Bodymovin 插件的流程如下:
Window > Extensions
中,你会看到安装好的扩展安装插件详见 http://airbnb.io/lottie/after-effects/bodymovin-installation.html
下面讲讲如何从 Sketch 开始,制作一个 lottie 动画文件。如果你使用 svg 图片,跳到步骤3。如果你使用 AI,跳到步骤4。需要准备好 Sketch,AI,AE,并安装好 Bodymovin 插件。下面开始:
.ai
文件.ai
文件导入到 AE 中更多细节可查看官方文档 Sketch/SVG/Illustrator to Lottie workflow
*AE 特性大部分已经支持,具体可以查看 Supported Features(支持列表),设计师应该避免使用不支持 AE 的特性。
目前开看,支持较好的属性有:
不完全支持的属性分类有:
*设计过程中的优化建议和注意事项 General tips & guidelines
首先当然是看开发文档。这里我简单说说其中 lottie-web 的使用。
可以通过 script 标签
<script src="https://cdnjs.com/libraries/bodymovin" type="text/javascript"></script>
或 npm 包 lottie-web 引用
npm i -S lottie-web
import lottie from 'lottie-web'
调用 loadAnimation()
const myLottie = lottie.loadAnimation({
container: document.querySelector('.img-area'),
renderer: 'svg',
name: 'myLottieAnim',
loop: true,
autoplay: true,
path: './assets/cycle_animation.json',
})
loadAnimation 的参数
名称 | 描述 |
---|---|
container | 用于渲染的容器,一般使用一个 div 即可 |
renderer | 渲染器,可以选择 ‘svg’ / ‘canvas’ / ‘html’,个人测试发现 svg 效果和兼容性最好 |
name | 动画名称,用于 reference |
loop | 循环 |
autoplay | 自动播放 |
path | json 路径,页面会通过一个 http 请求获取 json |
animationData | json 动画数据,与 path 互斥,建议使用 path,因为 animationData 会将数据打包进来,会使得 js bundle 过大 |
获取到 lottie 实例后,可以调用 api 控制动画,例如上述代码中可以使用
myLottie.pause()
相关 api
名称 | 参数 | 描述 |
---|---|---|
stop | 无 | 停止动画 |
play | 无 | 播放动画 |
pause | 无 | 暂停 |
setSpeed | Number | 设置播放速度,1 表示1倍速度,0.5 表示 0.5倍速度 |
setDirection | Number | 正反向播放,1 表示 正向,-1 表示反向 |
goToAndStop | Number, [Boolean] | 跳到某一帧或某一秒停止,第二个参数 iFrame 为是否基于帧模式还是时间,默认为 false |
goToAndPlay | Number, [Boolean] | 跳到某一帧或某一秒开始,第二个参数 iFrame 为是否基于帧模式还是时间,默认为 false |
playSegments | Array, [Boolean] | 播放片段,参数1为数组,两个元素为开始帧和结束帧;参数2为,是否立即播放片段,还是等之前的动画播放完成 |
destroy | 无 | 销毁 |
事件
也可以使用 addEventListener 监听以下事件
具体也可以查看组件文档
weex/rax 已经提供了 lottie 组件,由于是内部文档,暂不放链接。
api 支持没有 airbnb 官方完整,投入生产环境时还需要严格测试一下
使用@ali/rax-lottie 的 rax demo 如下
在我看来,追求更精细完美的动画体验一直是设计师和前端开发的使命。lottie 的出现可以替代传统的 gif,并且提供的 api 可以更好的控制动画。lottie 可能不适合用于过于复杂的大场景动画,但是局部的小动画,再适合不过了.
lottie 应该是一个发展趋势,甚至未来浏览器说不定就原生直接支持了这种 json 动画,设计和开发之间的壁垒也会越来越小.
在生产环境上迁移GitLab的目录需要注意一下几点:
目录的权限必须为755或者775
目录的用户和用户组必须为git:git
如果在深一级的目录下,那么git用户必须添加到上一级目录的账户。
很多文章说修改/etc/gitlab/gitlab.rb这个文件里面的git_data_dirsb
变量,其实没必要,只需要使用软链接改变原始目录/var/opt/gitlab/git-data更好一些.
注意:迁移前的版本和迁移后的版本必须保持一致, 如果迁移后的版本是高版本, 那么现在原版本做升级后再迁移.
迁移方法:
# 停止服务
gitlab-ctl stop
# 备份目录
mv /var/opt/gitlab/git-data{,_bak}
# 新建新目录
mkdir -p /data/service/gitlab/git-data
# 设置目录权限
chown -R git:git /data/service/gitlab
chmod -R 775 /data/service/gitlab
# 同步文件,使用rsync保持权限不变
rsync -av /var/opt/gitlab/git-data_bak/repositories /data/service/gitlab/git-data/
# 创建软链接
ln -s /data/service/gitlab/git-data /var/opt/gitlab/git-data
# 更新权限
gitlab-ctl upgrade
# 重新配置
gitlab-ctl reconfigure
# 启动gitlab服务
gitlab-ctl start
能够简单快捷地提供机器学习模型是从试验转向生产的关键挑战之一. 服务机器学习模型就是采用经训练的模型并使其能够应对预测请求的过程. 在生产中服务时, 您需要确保您的环境可重现.强制隔离并且是安全的. 为此, 提供机器学习模型的最简单方法之一是就是将 TensorFlow Serving 与 Docker 结合起来. Docker 是一种将软件打包成单元(我们称之为容器)的工具.其中包含运行软件所需的一切.
自 TensorFlow Serving 1.8 发布以来.我们一直在改进对 Docker 的支持. 我们现在提供 Docker images 用于 CPU 和 GPU 模型的服务和开发.为了解使用 TensorFlow Serving 部署模型究竟有多么容易.让我们尝试将 ResNet 模型投入生产. 此模型在 ImageNet 数据集上进行训练.并将 JPEG 镜像作为输入并返回镜像的分类类别.
我们的示例将假设您正在运行 Linux.不过它在 macOS 或 Windows 应该也可以运行.仅需少量修改.甚至不需要修改.
第一步安装 Docker CE. 这将为您提供运行和管理 Docker 容器所需的所有工具.
TensorFlow Serving 为其 ML 模型使用 SavedModel 格式.SavedModel 是一种语言中立的,可恢复的.密集的序列化格式.使更高级别的系统和工具能够生成.使用和转换 TensorFlow模型. 有几种方法可以导出 SavedModel(包括来自 Keras). 在本练习中.我们只需下载预先训练的 pre-trained ResNet SavedModel:
$ mkdir /tmp/resnet
$ curl -s https://storage.googleapis.com/download.tensorflow.org/models/official/20181001_resnet/savedmodels/resnet_v2_fp32_savedmodel_NHWC_jpg.tar.gz | tar --strip-components=2 -C /tmp/resnet -xvz
我们现在应该在/tmp/resnet
中有一个包含我们模型的文件夹.可以通过运行来验证这一点:
$ ls /tmp/resnet
1538687457
现在我们有了模型.使用 Docker 服务就像拉来最新发布的 TensorFlow Serving 来服务环境镜像一样简单.并将其指向模型:
$ docker pull tensorflow/serving
$ docker run -p 8501:8501 --name tfserving_resnet \
--mount type=bind,source=/tmp/resnet,target=/models/resnet \
-e MODEL_NAME=resnet -t tensorflow/serving &
...
... main.cc:327]在0.0.0.0:8500运行ModelServer ......
... main.cc:337]导出HTTP / REST API:localhost:8501 ...
分解命令行参数.我们:
-p 8501:8501: 将容器的端口 8501(TensorFlow 服务响应 REST API 请求)发布到主机的端口 8501
–name tfserving_resnet: 我们为容器创建名称为tfserving_resnet
.这样稍后我们可以作参考
–mount type=bind,source=/tmp/resnet,target=/models/resnet: 在容器(/models/resnet)上安装主机的本地目录(/tmp/resnet), 以便 TensorFlow 服务可以从容器内部读取模型.
-e MODEL_NAME=resnet: 告诉 TensorFlow Serving 下载名为resnet
的模型
-t tensorflow/serving: 基于服务镜像tensorflow/serving
运行 Docker 容器
接下来让我们下载 python 客户端脚本, 它将发送服务的模型镜像并获取预测, 我们还将测量服务器响应时间.
$ curl -o /tmp/resnet/resnet_client.py https://raw.githubusercontent.com/tensorflow/serving/master/tensorflow_serving/example/resnet_client.py
此脚本将下载猫的镜像并在测量响应时间时将其重复发送到服务器.如脚本的主循环中所示:
# The server URL specifies the endpoint of your server running the ResNet
# model with the name "resnet" and using the predict interface.
SERVER_URL = 'http://localhost:8501/v1/models/resnet:predict'
...
# Send few actual requests and time average latency.
total_time = 0
num_requests = 10
for _ in xrange(num_requests):
response = requests.post(SERVER_URL, data=predict_request)
response.raise_for_status()
total_time += response.elapsed.total_seconds()
prediction = response.json()['predictions'][0]
print('Prediction class: {}, avg latency: {} ms'.format(
prediction['classes'], (total_time*1000)/num_requests))
此脚本使用请求模块.因此如果您尚未安装.则需要安装它.通过运行此脚本.您应该看到如下所示的输出:
$ python /tmp/resnet/resnet_client.py
Prediction class: 282, avg latency: 185.644 ms
如您所见.使用 TensorFlow Serving 和 Docker 创建模型非常简单直白.您甚至可以创建自己的嵌入式模型的自定义 Docker 镜像. 以便更轻松地进行部署.
既然我们在 Docker 中提供了一个模型.您可能已经注意到来自 TensorFlow Serving 的日志消息如下所示:
Your CPU supports instructions that this TensorFlow binary was not compiled to use: AVX2 FMA
TensorFlow Serving 已发布的 Docker 镜像旨在竭尽所能来使用 CPU 架构.因此省略了一些优化以最大限度地提高兼容性.如果您没有看到此消息.则您的二进制文件可能已针对您的 CPU 进行了优化. 根据您的模型执行的操作.这些优化可能会对您的服务性能产生重大影响.值得庆幸的是.将您自己的优化服务镜像组合在一起非常简单.
首先, 我们要构建 TensorFlow Serving 的优化版本. 最简单的方法是构建官方的 TensorFlow Serving 开发环境 Docker 镜像. 这具有为镜像构建的系统自动生成优化的 TensorFlow 服务二进制文件的良好特性.为了区分我们创建的镜像和官方镜像, 我们将$USER/
添加到镜像名称之前.
让我们称这个开发镜像为 $USER/tensorflow-serving-devel:
$ docker build -t $USER/tensorflow-serving-devel \
-f Dockerfile.devel \
https://github.com/tensorflow/serving.git#:tensorflow_serving/tools/docker
构建 TensorFlow 服务开发镜像可能需要一段时间.具体取决于计算机的速度. 完成之后让我们使用优化的二进制文件构建一个新的服务镜像. 并将其命名为 $USER/tensorflow-serving:
$ docker build -t $USER/tensorflow-serving \
--build-arg TF_SERVING_BUILD_IMAGE=$USER/tensorflow-serving-devel \
https://github.com/tensorflow/serving.git#:tensorflow_serving/tools/docker
现在我们有了新的服务镜像.让我们再次启动服务器:
$ docker kill tfserving_resnet
$ docker run -p 8501:8501 --name tfserving_resnet \
--mount type=bind,source=/tmp/resnet,target=/models/resnet \
-e MODEL_NAME=resnet -t $USER/tensorflow-serving &
最后运行我们的客户端:
$ python /tmp/resnet/resnet_client.py
Prediction class: 282, avg latency: 84.8849 ms
在我们的机器上.我们看到使用原生优化二进制文件.每次预测平均加速超过100毫秒(119%). 在不同的机器(和型号)上您可能会看到不同的结果. 最后.随意销毁 TensorFlow Serving 容器:
$ docker stop tfserving_resnet Or docker kill tfserving_resnet
``````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````
不知道大家是怎样安排自己的日常计划的,我习惯是建立一个仓库,按照年/周记录在 markdown 里,平时这个仓库也写点简单的 demo,目录类似如下:
week
├── 2016
├── 2017
├── 2018
│ ├── 20180102.md
│ ├── 20180108.md
│ ├── 20180115.md
│ ├── 20180122.md
│ ├── 20180126.md
│ ├── ...
│ ├── ...
│ ├── ...
│ ├── 20181007.md
│ ├── pixi.md
│ ├── schedule.md
│ ├── temp.css
│ ├── temp.html
│ ├── temp.js
│ ├── temp.json
│ └── temp.md
├── package.json
└── yarn.lock
关于写倒计时大家可能都都比较熟悉,使用 setTimeout 或 setInterval 就可以搞定。几秒钟或者几分钟的倒计时这样写没有问题,但是如果是长时间的倒计时,这样写就会不准确。如果用户修改了他的设备时间,这样的倒计时就没有意义了。今天就说说写一个精确的倒计时的方法。
本文将介绍如何使用 JavaScript 创建文件,并自动/手动将文件下载。这在导出原始数据时会比较方便。
/**
* 创建并下载文件
* @param {String} fileName 文件名
* @param {String} content 文件内容
*/
function createAndDownloadFile(fileName, content) {
var aTag = document.createElement('a');
var blob = new Blob([content]);
aTag.download = fileName;
aTag.href = URL.createObjectURL(blob);
aTag.click();
URL.revokeObjectURL(blob);
}
很简单对吧,直接调用这个方法,传入文件名和文件内容,程序新建 a 标签,新建 Blob 对象,将文件名赋给 a 标签,同时将 Blob 对象作为 Url 也赋给 a 标签,模拟点击事件,自动下载成功,最后再回收内存。下面我们来看看具体是怎么操作的。