<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>as181920 (Andersen Fan)</title>
    <link>https://ruby-china.org/as181920</link>
    <description></description>
    <language>en-us</language>
    <item>
      <title>抄书篇 - 用 Ruby 执行一个 huggingface diffusers 模型来生成图片</title>
      <description>&lt;p&gt;之前群里有人提起 Ruby 的 AI 生态，这个领域还是 py 的天地，就跟前端是 js 的天地一样。&lt;/p&gt;

&lt;p&gt;但是有劳模&lt;a href="https://github.com/ankane" rel="nofollow" target="_blank" title=""&gt;Andrew Kane&lt;/a&gt;做了好多工作，各种常用库的封装，大模型的对接比如 huggingface 上 transformers 的调用&lt;a href="https://github.com/ankane/informers" rel="nofollow" target="_blank" title=""&gt;informers&lt;/a&gt;，这产出着实让人赞叹。&lt;/p&gt;

&lt;p&gt;实现的基本逻辑是使用&lt;a href="https://github.com/ankane/onnxruntime-ruby" rel="nofollow" target="_blank" title=""&gt;onnxruntime&lt;/a&gt;这个通用大模型格式作为底层调用，上面封装调用。&lt;/p&gt;

&lt;p&gt;对于有些没有 onnx 格式的（huggingface 上主要是 safetensors 格式较多），可以使用 optimum-cli 命令去一键转化格式。&lt;/p&gt;

&lt;p&gt;想体验下用文本生成图片，py 只需要&lt;a href="https://huggingface.co/docs/diffusers/tutorials/autopipeline" rel="nofollow" target="_blank" title=""&gt;五行代码&lt;/a&gt;，用 Ruby 发现没有现成的封装，再看一下 huggingface diffuser 的结构 (model_index.json) 是几个模型 (tokenizer/embedding/unet/vae) 组合起来使用，需要多个模型前后组合起来调用。怎么办，咱抄书，以下是抄书内容非原创，&lt;a href="https://huggingface.co/docs/diffusers/using-diffusers/write_own_pipeline" rel="nofollow" target="_blank" title=""&gt;原文在这里&lt;/a&gt;。&lt;/p&gt;

&lt;p&gt;(插一段题外，遥记得十几前考 pmp 的时候，讲师建议没把握的可以把书抄一遍，当初就是手抄了一遍，笨办法也是办法。)&lt;/p&gt;

&lt;p&gt;起步，先把大模型拿下来 (文档用的"CompVis/stable-diffusion-v1-4"，这里用的"stable-diffusion-v1-5/stable-diffusion-v1-5")&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;optimum-cli &lt;span class="nb"&gt;export &lt;/span&gt;onnx &lt;span class="nt"&gt;--model&lt;/span&gt; stable-diffusion-v1-5/stable-diffusion-v1-5 onnx
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;准备，来一段文本，这个我会&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;prompt &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"The godzilla is watching hello kitty doing her homework, they get along harmonious"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Next，模型准备好备用&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;text_encoder &lt;span class="o"&gt;=&lt;/span&gt; OnnxRuntime::Model.new&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"./onnx/text_encoder/model.onnx"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
unet &lt;span class="o"&gt;=&lt;/span&gt; OnnxRuntime::Model.new&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"./onnx/unet/model.onnx"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
vae_decoder &lt;span class="o"&gt;=&lt;/span&gt; OnnxRuntime::Model.new&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"./onnx/vae_decoder/model.onnx"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Next，把文本用 tokenizer 转换成 tokens&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;tokenizer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Tokenizers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;from_pretrained&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"openai/clip-vit-large-patch14"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;# openai/clip-vit-base-patch32&lt;/span&gt;
&lt;span class="n"&gt;tokenizer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;enable_padding&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;length: &lt;/span&gt;&lt;span class="mi"&gt;77&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;pad_id: &lt;/span&gt;&lt;span class="mi"&gt;49407&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;tokenizer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;enable_truncation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;77&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;text_tokens&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;tokenizer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;encode_batch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;prompt&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;text_ids&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Torch&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;tensor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;text_tokens&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="ss"&gt;:ids&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Next，把 tokens 做 embedding 生成模型输入需要的向量数据格式&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;text_embeddings&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Torch&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;no_grad&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;text_encoder&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;predict&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="ss"&gt;input_ids: &lt;/span&gt;&lt;span class="n"&gt;text_ids&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="c1"&gt;# Shape: 1x77&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;h&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="no"&gt;Torch&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;tensor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;h&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"last_hidden_state"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="c1"&gt;# Shape: 1x77x768&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Next，按 diffusers 设计加入 padding 数据&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;uncond_tokens&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;tokenizer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;encode_batch&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="s2"&gt;""&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;batch_size&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;uncond_ids&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Torch&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;tensor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;uncond_tokens&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="ss"&gt;:ids&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="n"&gt;uncond_embeddings&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;text_encoder&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;predict&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="ss"&gt;input_ids: &lt;/span&gt;&lt;span class="n"&gt;uncond_ids&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;h&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="no"&gt;Torch&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;tensor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;h&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"last_hidden_state"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="c1"&gt;# Shape: 1x77x768&lt;/span&gt;
&lt;span class="n"&gt;text_embeddings&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Torch&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;cat&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="n"&gt;uncond_embeddings&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;text_embeddings&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Next，创建 unet 模型生图用的初始 noise 数据&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;height&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;512&lt;/span&gt;
&lt;span class="n"&gt;width&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;512&lt;/span&gt;
&lt;span class="n"&gt;channels_num&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;unet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;inputs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;detect&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:name&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;"sample"&lt;/span&gt; &lt;span class="p"&gt;}[&lt;/span&gt;&lt;span class="ss"&gt;:shape&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="n"&gt;generator&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Torch&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Generator&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;manual_seed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;# Seed generator to create the initial latent noise&lt;/span&gt;
&lt;span class="no"&gt;Torch&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;manual_seed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;latents&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Torch&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;randn&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="n"&gt;batch_size&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;channels_num&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;height&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;width&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;generator&lt;/span&gt;&lt;span class="p"&gt;:,&lt;/span&gt; &lt;span class="ss"&gt;device: &lt;/span&gt;&lt;span class="no"&gt;DEVICE&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;# Shape: 1x4x64x64&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Next，unet 模型需要一个 scheduler 来进行一步步降噪并行成最终与 prompt 对应的图片。&lt;/p&gt;

&lt;p&gt;scheduler 实现靠“大模型自举”就是让 gpt 生成结果失败，最后只能照着 diffusers&lt;a href="https://github.com/huggingface/diffusers/blob/main/src/diffusers/schedulers/scheduling_pndm.py" rel="nofollow" target="_blank" title=""&gt;源码&lt;/a&gt;手抄一份&lt;a href="https://github.com/as181920/diffusers-ruby-demo/blob/main/pndm_scheduler.rb" rel="nofollow" target="_blank" title=""&gt;Ruby 版 PNDMScheduler&lt;/a&gt;。&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;scheduler&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;PNDMScheduler&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;steps_offset: &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;timestep_spacing: &lt;/span&gt;&lt;span class="s2"&gt;"leading"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;latents&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;latents&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;scheduler&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;init_noise_sigma&lt;/span&gt; &lt;span class="c1"&gt;# Scaling the input with the initial noise distribution, sigma&lt;/span&gt;
&lt;span class="n"&gt;num_inference_steps&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;25&lt;/span&gt; &lt;span class="c1"&gt;# denoising steps&lt;/span&gt;
&lt;span class="n"&gt;scheduler&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;num_inference_steps&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;num_inference_steps&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Next，主体部分，按 scheduler.timesteps 多次调用 unet 模型来 denoise 数据&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;guidance_scale&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;7.5&lt;/span&gt;
&lt;span class="n"&gt;scheduler&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;timesteps&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;each&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;timestep&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
  &lt;span class="n"&gt;latent_model_input&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Torch&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;cat&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="n"&gt;latents&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;scheduler&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;scale_model_input&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;timestep&lt;/span&gt;&lt;span class="p"&gt;:)&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="n"&gt;noise_pred&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Torch&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;no_grad&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;unet&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;predict&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="ss"&gt;sample: &lt;/span&gt;&lt;span class="n"&gt;latent_model_input&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;timestep: &lt;/span&gt;&lt;span class="no"&gt;Torch&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;tensor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;timestep&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="ss"&gt;encoder_hidden_states: &lt;/span&gt;&lt;span class="n"&gt;text_embeddings&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;h&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="no"&gt;Torch&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;tensor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;h&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"out_sample"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="c1"&gt;# Shape: 2x4x64x64&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="n"&gt;noise_pred_uncond&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;noise_pred_text&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;noise_pred&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;chunk&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;noise_pred&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;noise_pred_uncond&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;guidance_scale&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;noise_pred_text&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;noise_pred_uncond&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

  &lt;span class="n"&gt;latents&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;scheduler&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;step&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;noise_pred&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;timestep&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;latents&lt;/span&gt;&lt;span class="p"&gt;)[&lt;/span&gt;&lt;span class="ss"&gt;:prev_sample&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Next，数据生成好了，只需要调用 vae 把向量 decode 成 image 数据&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;latents&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;latents&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mf"&gt;0.18215&lt;/span&gt;
&lt;span class="n"&gt;image&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Torch&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;no_grad&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;vae_decoder&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;predict&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="ss"&gt;latent_sample: &lt;/span&gt;&lt;span class="n"&gt;latents&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;h&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="no"&gt;Torch&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;tensor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;h&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"sample"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="c1"&gt;# Shape: 1x3x512x512 这里可以看到转换成一张图片三个channel(RGB)及512像素&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Final，保存图。&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;image&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;image&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mf"&gt;2.0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mf"&gt;0.5&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;clip&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;image&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;image&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;image&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ndim&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;
&lt;span class="n"&gt;image&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;image&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;permute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;# 调整维度顺序，从 (C, H, W) 到 (H, W, C)&lt;/span&gt;
&lt;span class="n"&gt;image&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;image&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;255&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;round&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;to&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;Torch&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;uint8&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;# 转换到 uint8 并放大到 [0, 255]&lt;/span&gt;
&lt;span class="n"&gt;output_height&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;output_width&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_channels&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;image&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;shape&lt;/span&gt;
&lt;span class="n"&gt;png&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;ChunkyPNG&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Image&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;output_width&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;output_height&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;height&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;times&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
  &lt;span class="n"&gt;width&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;times&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
    &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;g&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;image&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;..&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="ss"&gt;:to_i&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;# 取 RGB 值&lt;/span&gt;
    &lt;span class="n"&gt;png&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;ChunkyPNG&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Color&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;rgb&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;g&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="n"&gt;png&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;save&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"./output-rb.png"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;在老旧笔记本上 (Linux Intel 9th i7，未使用 GPU) 同时执行 ruby 和 python 脚本的耗时&lt;/p&gt;

&lt;p&gt;Ruby:&lt;/p&gt;

&lt;p&gt;real 4m11.149s    user 21m52.781s    sys 0m8.744s&lt;/p&gt;

&lt;p&gt;python:&lt;/p&gt;

&lt;p&gt;real 4m18.794s    user 23m1.629s    sys 0m55.203s &lt;/p&gt;

&lt;p&gt;其中时间有差异，应该是 onnx 自身优化的缘故，都是大模型的耗时，与调用语言关系不大。&lt;/p&gt;

&lt;p&gt;结果&lt;/p&gt;

&lt;p&gt;&lt;img src="https://l.ruby-china.com/photo/as181920/b7ac41e6-06bc-48a6-a109-92b707cec5ee.png!large" title="" alt=""&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/as181920/diffusers-ruby-demo/" rel="nofollow" target="_blank" title=""&gt;原始代码在这里供参考&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;其它，&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;图像不好看，剩下就是模型调优和结构设计修改的事情，调用代码还是差不多的。只是如果替换或者修改了其中组件，一般就需要补上训练过程，因为预训练的 text2image 的权重参数对应关系已经不适用了。上面生成图片没有原始文档中生成的好看，大致是因为 tokenizer 不完全一致的缘故。&lt;/li&gt;
&lt;li&gt;设置 seed 是为了生成相同的数据方便 debug（比如跟 python 数据 step by step 进行比对这种笨办法）&lt;/li&gt;
&lt;li&gt;cuda 本地安装编译支持 cpu 版本，不强制依赖 gpu 设备&lt;/li&gt;
&lt;li&gt;PNDMScheduler 只调试了当前 demo 执行到的部分，其余部分可能有 bug，DDIMScheduler 未使用和测试。&lt;/li&gt;
&lt;li&gt;使用 GPU(cuda) 会快很多，几乎不用等，需要在各个 Torch.tensor(...) 加 to("cuda")，偷懒未做适配。&lt;/li&gt;
&lt;/ul&gt;</description>
      <author>as181920</author>
      <pubDate>Fri, 10 Jan 2025 23:43:13 +0800</pubDate>
      <link>https://ruby-china.org/topics/44014</link>
      <guid>https://ruby-china.org/topics/44014</guid>
    </item>
    <item>
      <title>使用 Turbo 处理 modal 弹框和下拉加载的方法和遗留问题</title>
      <description>&lt;p&gt;从 Turbolinks 升级到 Turbo 后，先升级后补课中，2 个常见功能的处理：&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;手机端常见的下拉到底部加载下一页，之前做法是 js 判断到底部后，remote get 下一页数据，通过 js.erb 来 append html。使用 turbo frame 可以不写 js，不改动 controller 的情况下，直接 view 层 list 展示时，通过嵌套 lazy load turbo_frame，当其进入视窗自动加载来实现。&lt;/li&gt;
&lt;/ol&gt;
&lt;pre class="highlight erb"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;%=&lt;/span&gt; &lt;span class="n"&gt;turbo_frame_tag&lt;/span&gt; &lt;span class="s2"&gt;"posts_&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="vi"&gt;@posts.current_page&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;
  &lt;span class="cp"&gt;&amp;lt;%=&lt;/span&gt; &lt;span class="n"&gt;render&lt;/span&gt; &lt;span class="s2"&gt;"post_list"&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;

  &lt;span class="cp"&gt;&amp;lt;%&lt;/span&gt; &lt;span class="k"&gt;unless&lt;/span&gt; &lt;span class="vi"&gt;@posts.last_page&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;
    &lt;span class="cp"&gt;&amp;lt;%&lt;/span&gt; &lt;span class="n"&gt;turbo_frame_tag&lt;/span&gt; &lt;span class="s2"&gt;"posts_&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="vi"&gt;@posts.next_page&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;loading: :lazy&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;src: &lt;/span&gt;&lt;span class="n"&gt;url_for&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;page: &lt;/span&gt;&lt;span class="vi"&gt;@posts.next_page&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;div&amp;gt;&lt;/span&gt;loading...&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
    &lt;span class="cp"&gt;&amp;lt;%&lt;/span&gt; &lt;span class="k"&gt;end&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;
  &lt;span class="cp"&gt;&amp;lt;%&lt;/span&gt; &lt;span class="k"&gt;end&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;
&lt;span class="cp"&gt;&amp;lt;%&lt;/span&gt; &lt;span class="k"&gt;end&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;遗留问题：某些场景下拉到底部显示加载中的等待体验不满足客户需求，需要在下拉到一半时就预先加载下一页，还没有想到处理方法。&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;常见的点击编辑按钮弹出 modal 的情况，之前同样是 remote get 后通过 js.erb 来 append modal 框。turbo 可以通过 link 的 data 属性指向 trubo frame id 来 load modal 内容 (弹出 modal)&lt;/li&gt;
&lt;/ol&gt;
&lt;pre class="highlight erb"&gt;&lt;code&gt;&lt;span class="c"&gt;&amp;lt;%# index.html.erb %&amp;gt;&lt;/span&gt;
&lt;span class="cp"&gt;&amp;lt;%=&lt;/span&gt; &lt;span class="n"&gt;link_to&lt;/span&gt; &lt;span class="s2"&gt;"edit"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:edit&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;post&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="ss"&gt;data: &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;turbo_frame: &lt;/span&gt;&lt;span class="s2"&gt;"modal_frame_id"&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;
&lt;span class="cp"&gt;&amp;lt;%=&lt;/span&gt; &lt;span class="n"&gt;turbo_frame_tag&lt;/span&gt; &lt;span class="s2"&gt;"modal_frame_id"&lt;/span&gt; &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;target: &lt;/span&gt;&lt;span class="s2"&gt;"_top"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;class: &lt;/span&gt;&lt;span class="s2"&gt;"modal_frame"&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;pre class="highlight erb"&gt;&lt;code&gt;&lt;span class="c"&gt;&amp;lt;%# edit.html.erb%&amp;gt;&lt;/span&gt;
&lt;span class="cp"&gt;&amp;lt;%=&lt;/span&gt; &lt;span class="n"&gt;turbo_frame_tag&lt;/span&gt; &lt;span class="s2"&gt;"modal_frame_id"&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"modal"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;div&amp;gt;&lt;/span&gt;modal content&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
    &lt;span class="cp"&gt;&amp;lt;%=&lt;/span&gt; &lt;span class="n"&gt;button_tag&lt;/span&gt; &lt;span class="s2"&gt;"Cancel"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;data: &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;dismiss: &lt;/span&gt;&lt;span class="s2"&gt;"modal"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="n"&gt;onclick&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"this.closest('.modal_frame').src='';this.closest('.modal').remove();"&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;span class="cp"&gt;&amp;lt;%&lt;/span&gt; &lt;span class="k"&gt;end&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;遗留问题：由于是 get 请求，重复点击时候如何重新渲染没解决，通过 src 清空临时处理；这里 dismiss 自动关闭没生效，通过 onclick 临时 remove 内容；想知道正确的处理方法。&lt;/p&gt;</description>
      <author>as181920</author>
      <pubDate>Mon, 28 Jun 2021 11:06:05 +0800</pubDate>
      <link>https://ruby-china.org/topics/41417</link>
      <guid>https://ruby-china.org/topics/41417</guid>
    </item>
    <item>
      <title>[上海][张江] 服务商家的创新应用，诚邀 Ruby 开发伙伴。</title>
      <description>&lt;p&gt;本人目前是团队技术人员，诚邀 Ruby 开发同事。&lt;/p&gt;
&lt;h2 id="背景："&gt;背景：&lt;/h2&gt;
&lt;p&gt;传统互联网创业公司，之前基于微信开发各种行业解决方案，包括微电商；
创业公司难免不断试点新业务来探索市场机会，目前探索做一款基于扫码，服务商户防伪溯源营销等等需求的应用；
公司没有 BAT 的大而成熟，也不是初始创业纯花钱，个人认为尚算比较健康。&lt;/p&gt;
&lt;h2 id="需求："&gt;需求：&lt;/h2&gt;
&lt;p&gt;基于 Ruby 技术的开发伙伴 2～3 人；
需要能独立开发中小应用（要求编程语言、常规 DB 知识，服务器基础、前段基础、代码库操作等）所需要的常规技能；能持续学习；
也需要一位有一定项目经验，技术工作为主的同时，能负责应用开发到部署维护运营迭代全过程，并能够应对规模化后遇到的各种性能和拓展需求；协调团队工作和各部门对接，计划管理等。&lt;/p&gt;
&lt;h2 id="提供："&gt;提供：&lt;/h2&gt;
&lt;p&gt;常规薪资和福利（基于市场行情），具体可与 hr 详聊；
在保证业务产品需求实现和技术产品质量的前提下，尊重技术人员的技术偏好等。&lt;/p&gt;
&lt;h2 id="加分项："&gt;加分项：&lt;/h2&gt;
&lt;p&gt;有独立项目经验；
技术 blog 或者 github 分享库;
代码整洁偏好。&lt;/p&gt;
&lt;h2 id="联系方式："&gt;联系方式：&lt;/h2&gt;
&lt;p&gt;官方：hr@vcooline.com
如果需要非正式的咨询沟通，可以个人 as181920@gmail.com&lt;/p&gt;

&lt;p&gt;ps: 公司 hr 官方招聘帖见：&lt;a href="https://ruby-china.org/topics/27621" title=""&gt;https://ruby-china.org/topics/27621&lt;/a&gt;&lt;/p&gt;</description>
      <author>as181920</author>
      <pubDate>Tue, 13 Oct 2015 11:00:27 +0800</pubDate>
      <link>https://ruby-china.org/topics/27651</link>
      <guid>https://ruby-china.org/topics/27651</guid>
    </item>
    <item>
      <title>资讯家庭空气检测 DIY 有木有，比如基于树莓派</title>
      <description>&lt;p&gt;如题，想检测关注家庭的空气、甲醛情况等，不买线程设备，有没有基于树莓派（有了 linux 系统什么都可以干，又不需要性能）之类设备，加购买检测模块，自己构建的方法&lt;/p&gt;

&lt;p&gt;主要是方便后续扩展各种自己需要的检测，和居家简易智能需求。&lt;/p&gt;

&lt;p&gt;但是网上找相关模块，没有找到甲醛这块的，不熟悉相关领域，不敢随意购买（要钱呀）&lt;/p&gt;

&lt;p&gt;有了解的可以给提点下。&lt;/p&gt;</description>
      <author>as181920</author>
      <pubDate>Sun, 22 Mar 2015 22:03:55 +0800</pubDate>
      <link>https://ruby-china.org/topics/24781</link>
      <guid>https://ruby-china.org/topics/24781</guid>
    </item>
    <item>
      <title>kaminari 等分页时 count 大表太慢的时候，大家是如何处理的</title>
      <description>&lt;p&gt;如题，&lt;/p&gt;

&lt;p&gt;时光流逝，人岁渐长，表也越来越大，翻页默认要 count，就越来越慢了。大家一般哪些处理方式？&lt;/p&gt;

&lt;p&gt;比如取消总页数显示（无限下拉），但是后台大家还是习惯传统的分页显示。&lt;/p&gt;</description>
      <author>as181920</author>
      <pubDate>Wed, 04 Feb 2015 14:07:41 +0800</pubDate>
      <link>https://ruby-china.org/topics/24114</link>
      <guid>https://ruby-china.org/topics/24114</guid>
    </item>
    <item>
      <title>大家 MySQL 如何存储 emoji 的？</title>
      <description>&lt;p&gt;emoji 字符在 postgresql 中一切正常，mysql 中需要用 utf8mb4 格式，不知道会有多少影响。&lt;/p&gt;

&lt;p&gt;目前由于使用云数据库，基本只能用 mysql 了，没找到比较好的解决方案，有遇到过的给个参考意见：）&lt;/p&gt;</description>
      <author>as181920</author>
      <pubDate>Mon, 02 Feb 2015 13:37:39 +0800</pubDate>
      <link>https://ruby-china.org/topics/24066</link>
      <guid>https://ruby-china.org/topics/24066</guid>
    </item>
    <item>
      <title>咨询个，大家对已成型项目如何维护和持续开发</title>
      <description>&lt;p&gt;1，成型项目，代码量多，质量参差不齐，正好没测试覆盖，这个时候持续加功能代码经常不稳定，以及低效率。这个时候大家一般会有哪些处理方式方法，提供个思路。&lt;/p&gt;

&lt;p&gt;2，如果你很幸运做了一个工程有人用，一两年后，数据量大了，性能问题来了，其中有一些是简单加 db 索引和优化 sql 无法解决的，大家又有哪些处理的方式？&lt;/p&gt;</description>
      <author>as181920</author>
      <pubDate>Mon, 22 Dec 2014 13:35:14 +0800</pubDate>
      <link>https://ruby-china.org/topics/23332</link>
      <guid>https://ruby-china.org/topics/23332</guid>
    </item>
    <item>
      <title>哪位有经验的分享下 rabbitmq 和 zeromq 什么情况应该用哪个</title>
      <description>&lt;p&gt;如题
除了自己找文档，想看看有没有使用过的经验。貔貅源码里面看到用的是 rabbitmq。&lt;/p&gt;</description>
      <author>as181920</author>
      <pubDate>Mon, 29 Sep 2014 14:48:46 +0800</pubDate>
      <link>https://ruby-china.org/topics/21780</link>
      <guid>https://ruby-china.org/topics/21780</guid>
    </item>
    <item>
      <title>God terminate 总是关不掉进程</title>
      <description>&lt;p&gt;试用 god 来运行和管理一些后台常驻进程和任务，&lt;/p&gt;

&lt;p&gt;简单如这几行&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="no"&gt;God&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;watch&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;w&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
  &lt;span class="n"&gt;w&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"logstash_agent"&lt;/span&gt;
  &lt;span class="n"&gt;w&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;start&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"cd /dyne/apps/logstash-1.4.2 &amp;amp;&amp;amp; bin/logstash -f config/agent.conf"&lt;/span&gt;
  &lt;span class="n"&gt;w&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"/home/git/logs/logstash_agent.log"&lt;/span&gt;
  &lt;span class="n"&gt;w&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;keepalive&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;有 java 的，有 rackup 运行 thin 的，有 ruby 死循环+sleep 的，当需要关闭 terminate 的时候，进程很多都没有关掉，&lt;/p&gt;

&lt;p&gt;如果 restart 的话，通常就是多起一个进程。&lt;/p&gt;

&lt;p&gt;哪里姿势不对？&lt;/p&gt;</description>
      <author>as181920</author>
      <pubDate>Mon, 14 Jul 2014 23:27:02 +0800</pubDate>
      <link>https://ruby-china.org/topics/20494</link>
      <guid>https://ruby-china.org/topics/20494</guid>
    </item>
    <item>
      <title>纯 ruby 代码常驻进程怎么写的，daemon 这个 gem 好久没动了</title>
      <description>&lt;p&gt;如题，纯 ruby 代码，监听队列，常驻进程，一般怎么写的？&lt;/p&gt;</description>
      <author>as181920</author>
      <pubDate>Fri, 06 Jun 2014 17:39:14 +0800</pubDate>
      <link>https://ruby-china.org/topics/19789</link>
      <guid>https://ruby-china.org/topics/19789</guid>
    </item>
    <item>
      <title>开发模式静态文件加载时间超长问题有遇到的么</title>
      <description>&lt;p&gt;&lt;img src="//l.ruby-china.com/photo/2014/989ab356e4435778764bd96cab3f324e.png" title="" alt=""&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src="//l.ruby-china.com/photo/2014/a95c94dfe3e1ffa51e71ebd821656601.png" title="" alt=""&gt;&lt;/p&gt;

&lt;p&gt;开发模式，程序后台运行时间正常，浏览器显示页面超慢，看了下是静态文件加载要到好几秒，不清楚缘故&lt;/p&gt;</description>
      <author>as181920</author>
      <pubDate>Sat, 22 Feb 2014 22:24:45 +0800</pubDate>
      <link>https://ruby-china.org/topics/17422</link>
      <guid>https://ruby-china.org/topics/17422</guid>
    </item>
    <item>
      <title>方式参考：好多网页没看？，发到电子书坐地铁时慢慢看</title>
      <description>&lt;p&gt;有个习惯问题，就是查网页，结果一堆网页要去看，时间就耗进去了。&lt;/p&gt;

&lt;p&gt;如果正好有个 kindle，又正好要蛮长时间坐地铁，可以装浏览器控件，点一下自动就发到电子书，&lt;/p&gt;

&lt;p&gt;于是利用地铁的时间干掉这些待看的网页。&lt;/p&gt;</description>
      <author>as181920</author>
      <pubDate>Fri, 21 Feb 2014 16:19:33 +0800</pubDate>
      <link>https://ruby-china.org/topics/17401</link>
      <guid>https://ruby-china.org/topics/17401</guid>
    </item>
    <item>
      <title>&lt;%%= xxx %&gt;双百分号啥个意思</title>
      <description>&lt;p&gt;有 html 代码加了 js 后好像可以动态写一些 template，如&lt;/p&gt;

&lt;p&gt;
  &amp;lt;%for(var i=0,il=data.length;i&amp;lt;il&amp;gt;
...

&amp;lt;p&amp;gt;这个时候在 erb 文件里面会有问题，改成&amp;amp;lt;%% xxx %&amp;amp;gt;即可，这种双百分号是啥个意思？我肯定漏了哪个基础知识哈。&amp;lt;/p&amp;gt;
&amp;lt;/il&amp;gt;&lt;/p&gt;</description>
      <author>as181920</author>
      <pubDate>Fri, 21 Feb 2014 16:15:40 +0800</pubDate>
      <link>https://ruby-china.org/topics/17400</link>
      <guid>https://ruby-china.org/topics/17400</guid>
    </item>
    <item>
      <title>泛泛而问，权限管理一般大家用什么方法</title>
      <description>&lt;p&gt;权限处理，有 cancan，但不适用所有场景，还有它的维护情况。&lt;/p&gt;

&lt;p&gt;一般大家是用什么方式做权限控制的，或者怎么放置权限相关代码的位置？或者自己手写一般哪几个模块设计？&lt;/p&gt;</description>
      <author>as181920</author>
      <pubDate>Thu, 20 Feb 2014 13:26:34 +0800</pubDate>
      <link>https://ruby-china.org/topics/17371</link>
      <guid>https://ruby-china.org/topics/17371</guid>
    </item>
    <item>
      <title>升级 4.0.1 到 4.1.0.beta1 的个别细节</title>
      <description>&lt;p&gt;原由是想使用 4.1 的 enum 的功能，于是升级。主要参照官网升级说明，还有个别细节：&lt;/p&gt;

&lt;p&gt;1，Gemfile，rails 版本改成了 4.1.0.beta1，加了 gem：spring，spring-commands-rspec&lt;/p&gt;

&lt;p&gt;2，ransack 默认版本在 4.1 下有问题，需要改成 gem：
    gem "ransack", github: "activerecord-hackery/ransack", branch: "rails-4.1"
    gem "polyamorous", github: "activerecord-hackery/polyamorous"&lt;/p&gt;

&lt;p&gt;3，model 中 has_many through source 的 source 原来跟 string 可以，现在要改成 symbole 才行&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt; has_many :notes, through: :ownerships, source: 'item', source_type: 'Note'&lt;/li&gt;
&lt;li&gt;has_many :notes, through: :ownerships, source: :item, source_type: 'Note'&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;4，config/application.rb 里面由于不是 require "rails/all"(测试库用 rspec 了)，所以要加
 # Pick the frameworks you want:
+require "active_model/railtie"
 require "active_record/railtie"
 require "action_controller/railtie"
 require "action_mailer/railtie"
+require "action_view/railtie"
 require "sprockets/railtie"
 # require "rails/test_unit/railtie"&lt;/p&gt;

&lt;p&gt;5，guard 配置中让 rspec 用上 spring
-guard :rspec do
+guard :rspec, cmd:"spring rspec" do&lt;/p&gt;

&lt;p&gt;6，i18n 遇到问题的考虑 Gemfile 设置（临时解决 i18n 本身 bug）
gem 'i18n', github: 'svenfuchs/i18n'
gem 'rails', '4.1.0.beta1'&lt;/p&gt;

&lt;p&gt;另：上次升级用 ruby2.1 遇到基础库 pg 的莫名报错，这次 rvm reinstall 后还没有遇到问题。&lt;/p&gt;

&lt;p&gt;附官方文档：
&lt;a href="http://edgeguides.rubyonrails.org/4_1_release_notes.html" rel="nofollow" target="_blank"&gt;http://edgeguides.rubyonrails.org/4_1_release_notes.html&lt;/a&gt;
&lt;a href="http://edgeguides.rubyonrails.org/upgrading_ruby_on_rails.html" rel="nofollow" target="_blank"&gt;http://edgeguides.rubyonrails.org/upgrading_ruby_on_rails.html&lt;/a&gt;&lt;/p&gt;</description>
      <author>as181920</author>
      <pubDate>Fri, 14 Feb 2014 11:14:16 +0800</pubDate>
      <link>https://ruby-china.org/topics/17249</link>
      <guid>https://ruby-china.org/topics/17249</guid>
    </item>
    <item>
      <title>对于客户只有一台 windows 服务器且限定 sql server 的时候，怎么处理</title>
      <description>&lt;p&gt;1，windows 下也可以 rails
2，直接用 jruby 那套
3，直接.net 或者 php 开发&lt;/p&gt;

&lt;p&gt;前两个都可以，但是 gem 之类，略有麻烦和些许坑要做，不确定性能（以前都在 linux 下做，经验也不多），不放心；&lt;/p&gt;

&lt;p&gt;目前考虑用 3 号方法。大家是怎么弄的。（除了和客户 pk）&lt;/p&gt;</description>
      <author>as181920</author>
      <pubDate>Fri, 01 Nov 2013 11:52:38 +0800</pubDate>
      <link>https://ruby-china.org/topics/15209</link>
      <guid>https://ruby-china.org/topics/15209</guid>
    </item>
    <item>
      <title>纯 omniauth 的用户系统，本地调试怎么弄</title>
      <description>&lt;p&gt;工程里面没有自身用户系统，用户全部走 omniauth 外站验证，返回的都是外站配置时留的 url，回不到本地 localhost 地址，那本地日常调试使用什么方法最简便&lt;/p&gt;</description>
      <author>as181920</author>
      <pubDate>Fri, 01 Nov 2013 11:47:50 +0800</pubDate>
      <link>https://ruby-china.org/topics/15208</link>
      <guid>https://ruby-china.org/topics/15208</guid>
    </item>
    <item>
      <title>移动端存客户端数据，cookie 和 localStorage 现在哪个兼容性更好。</title>
      <description>&lt;p&gt;如题，对于手机端的浏览器（手机自带，chrome，opera，qq，360 等常用手机浏览器），以及微信自带的浏览器。&lt;/p&gt;

&lt;p&gt;保存一些客户端数据，cookie 和 localStorage 现在用哪个比较好比较兼容&lt;/p&gt;</description>
      <author>as181920</author>
      <pubDate>Fri, 01 Nov 2013 11:45:29 +0800</pubDate>
      <link>https://ruby-china.org/topics/15207</link>
      <guid>https://ruby-china.org/topics/15207</guid>
    </item>
    <item>
      <title>请教动态内容多的网页，用哪个框架或者控件比较好</title>
      <description>&lt;p&gt;以前都是简单的页面效果，自己谢谢 coffeescript 就好了。&lt;/p&gt;

&lt;p&gt;现在有个，页面上很多地方都是动态的，比如上面下拉框选一个，下面图表自动更新，动态切换图表类型。此外页面上各个区块经常要动态换内容。
这种手写 js，函数多了可能就会乱，与其自己抽取框架，不如先看看现成框架。&lt;/p&gt;

&lt;p&gt;大家有什么推荐使用？
extjs，很多控件有固定式样吧，网页都要自己定义式样，可能修改起来也麻烦；&lt;/p&gt;

&lt;p&gt;是直接 pjax 还是用什么 js 框架（angularjs 之类？）&lt;/p&gt;

&lt;p&gt;另外复杂一点的图表有哪些控件可选？目前只用过 highchart，不打算用水晶报表。&lt;/p&gt;</description>
      <author>as181920</author>
      <pubDate>Wed, 30 Oct 2013 17:23:21 +0800</pubDate>
      <link>https://ruby-china.org/topics/15160</link>
      <guid>https://ruby-china.org/topics/15160</guid>
    </item>
    <item>
      <title>微信，菜单，json，编码问题</title>
      <description>&lt;p&gt;微信的自定义菜单的接口要的是 json 格式数据，但是每次 post 过去只要是中文就报（英文都正常）：
{"errcode":40033,"errmsg":"invalid charset. please check your request, if include \uxxxx will create fail!"}
没找到怎么处理，有经验的给个解决建议？&lt;/p&gt;

&lt;p&gt;详细：
微信接口说明：&lt;a href="http://mp.weixin.qq.com/wiki/index.php?title=%E8%87%AA%E5%AE%9A%E4%B9%89%E8%8F%9C%E5%8D%95%E6%8E%A5%E5%8F%A3" rel="nofollow" target="_blank"&gt;http://mp.weixin.qq.com/wiki/index.php?title=%E8%87%AA%E5%AE%9A%E4%B9%89%E8%8F%9C%E5%8D%95%E6%8E%A5%E5%8F%A3&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src="//l.ruby-china.com/photo/38dd6090a085d75758ab426aa9137255.png" title="" alt=""&gt;&lt;/p&gt;

&lt;p&gt;接口代码：
res = HTTParty.post "&lt;a href="https://api.weixin.qq.com/cgi-bin/menu/create?access_token=#access_token}" rel="nofollow" target="_blank"&gt;https://api.weixin.qq.com/cgi-bin/menu/create?access_token=#access_token}&lt;/a&gt;{", body: &lt;a href="/ui_menu_json" class="user-mention" title="@ui_menu_json"&gt;&lt;i&gt;@&lt;/i&gt;ui_menu_json&lt;/a&gt;, headers: {'ContentType' =&amp;gt; 'application/json'}&lt;/p&gt;

&lt;p&gt;其中&lt;a href="/ui_menu_json" class="user-mention" title="@ui_menu_json"&gt;&lt;i&gt;@&lt;/i&gt;ui_menu_json&lt;/a&gt;是 Jbuilder gem 生成。其实遇到“中文”.to_json 这种、\uxxx 结果的文字，同步的时候都会报错，不知道什么缘故。&lt;/p&gt;</description>
      <author>as181920</author>
      <pubDate>Sun, 20 Oct 2013 01:11:14 +0800</pubDate>
      <link>https://ruby-china.org/topics/14873</link>
      <guid>https://ruby-china.org/topics/14873</guid>
    </item>
  </channel>
</rss>
