AI on Mac Studio – 1: Running fuyu-8B on mac studio

This is the first article about running an AI model on Mac Studio, and I will continue to migrate the environment from CUDA / Nvidia GPU to Mac MPS.

Why did I choose Mac Studio?

I chose Mac Studio because it is less expensive. It has 192GB of memory that can be used as a GPU. This means that it is possible to migrate the program from Nvidia GPU and save some money for personal use.

What is Fuyu-8B?

We are releasing Fuyu-8B, a small version of the multimodal model that powers our product. The model is available on HuggingFace. We think Fuyu-8B is exciting because:

  1. It has a much simpler architecture and training procedure than other multimodal models, making it easier to understand, scale, and deploy.
  2. It is designed from the ground up for digital agents, so it can support arbitrary image resolutions, answer questions about graphs and diagrams, answer UI-based questions, and perform fine-grained localization on screen images.
  3. It is fast – we can get responses for large images in less than 100 milliseconds.
  4. Despite being optimized for our use case, it performs well at standard image understanding benchmarks such as visual question-answering and natural-image-captioning.

Ok, let’s do it now.

Prepare the environment:

  1. You need Python 3.6+ and virtualenv installed. Conda or venv also work.

cssCopy code

virtualenv -p python3 py3

  1. Download the HuggingFace transformers and clone the transformer from GitHub.

bashCopy code

git clone https://github.com/huggingface/transformers.git cd transformers pip install .

  1. Download the model from https://huggingface.co/adept/fuyu-8b. You can use clone or download it manually, depending on your network speed.
  2. Install PyTorch from this page https://pytorch.org/get-started/locally/ depending on your environment. Since I am using Mac M2 Studio, I should install this:

Copy code

pip3 install torch torchvision torchaudio

  1. You are almost done here; now we can start the samples.

Sample 1:

from transformers import FuyuProcessor, FuyuForCausalLM
from PIL import Image
# load model and processor
model_id = "."
processor = FuyuProcessor.from_pretrained(model_id)
model = FuyuForCausalLM.from_pretrained(model_id, device_map="mps", torch_dtype=torch.float16)
# prepare inputs for the model
text_prompt = "Generate a coco-style caption.\n"
image_path = "bus.png"  # https://huggingface.co/adept-hf-collab/fuyu-8b/blob/main/bus.png
image = Image.open(image_path)
inputs = processor(text=text_prompt, images=image, return_tensors="pt")
for k, v in inputs.items():
    inputs[k] = v.to("mps")
# autoregressively generate text
generation_output = model.generate(**inputs, max_new_tokens=7)
generation_text = processor.batch_decode(generation_output[:, -7:], skip_special_tokens=True)
print(generation_text)

Sample 2:

import os
from transformers import FuyuProcessor, FuyuForCausalLM
from PIL import Image
import torch
def list_files_in_directory(path, extensions=[".png", ".jpeg", ".jpg", ".JPG", ".PNG", ".JPEG"]):
    files = [f for f in os.listdir(path) if os.path.isfile(os.path.join(path, f)) and any(f.endswith(ext) for ext in extensions)]
    return files
def main():
    # load model and processor
    model_id = "." #adept/fuyu-8b"
    processor = FuyuProcessor.from_pretrained(model_id)
    model = FuyuForCausalLM.from_pretrained(model_id, device_map="mps", torch_dtype=torch.float16) # To solve OOM, float16 enables operation with only 24GB of VRAM. Alternatively float16 can be replaced with bfloat16 with differences in loading time and inference time.
    # Load last image path or ask user
    try:
        with open("last_path.txt", "r") as f:
            last_path = f.read().strip()
        user_input = input(f"Do you want to use the last path '{last_path}'? (yes/no, default yes): ")
        if not user_input or user_input.lower() != 'no':
            last_path = last_path
        else:
            raise ValueError("User chose to input a new path.")
    except:
        last_path = input("Please provide the image directory path: ")
        with open("last_path.txt", "w") as f:
            f.write(last_path)
    while True:
        # List the first 10 images in the directory
        images = list_files_in_directory(last_path)[:10]
        for idx, image in enumerate(images, start=1):
            print(f"{idx}. {image}")
        # Allow the user to select an image
        image_choice = input(f"Choose an image (1-{len(images)}) or enter its name: ")
        try:
            idx = int(image_choice)
            image_path = os.path.join(last_path, images[idx-1])
        except ValueError:
            image_path = os.path.join(last_path, image_choice)
        try:
            image = Image.open(image_path)
        except:
            print("Cannot open the image. Please check the path and try again.")
            continue
        questions = [
            "Generate a coco-style caption.",
            "What color is the object?",
            "Describe the scene.",
            "Describe the facial expression of the character.",
            "Tell me about the story from the image.",
            "Enter your own question"
        ]
        # Asking the user to select a question from list, or select to input one
        for idx, q in enumerate(questions, start=1):
            print(f"{idx}. {q}")
        q_choice = int(input("Choose a question or enter your own: "))
        if q_choice <= 5:
            text_prompt = questions[q_choice-1] + '\n'
        else:
            text_prompt = input("Please enter your question: ") + '\n'
        while True: # To enable the user to ask further question about an image
            inputs = processor(text=text_prompt, images=image, return_tensors="pt")
            for k, v in inputs.items():
                inputs[k] = v.to("mps")
            # To eliminate attention_mask warning
            inputs["attention_mask"] = torch.ones(inputs["input_ids"].shape, device="mps")
            generation_output = model.generate(**inputs, max_new_tokens=50, pad_token_id=model.config.eos_token_id)
            generation_text = processor.batch_decode(generation_output[:, -50:], skip_special_tokens=True)
            print("Answer:", generation_text[0])
            text_prompt = input("Ask another question about the same image or type '/exit' to exit: ") + '\n'
            if text_prompt.strip() == '/exit':
                break
#if name == "main":
main()

yes, It is Chinese. But not the Chinese fuyu-7b knows. It is not “食” (eating) , but “我不想洗碗”( i don’t want to wash the dishes). Fuyu-7b is lying. lol.

redsocks升级记录

一直在用redsocks做流量转发,用着还是挺不错的,但是偶尔还是会hang住,导致网络卡住。老早就有升级的想法,一直没有动手。

什么是redsocks?

This tool allows you to redirect any TCP connection to SOCKS or HTTPS proxy using your firewall, so redirection may be system-wide or network-wide.

When is redsocks useful?

  • you want to route part of TCP traffic via OpenSSH DynamicForward Socks5 port using firewall policies. That was original redsocks development goal;
  • you use DVB ISP and this ISP provides internet connectivity with some special daemon that may be also called “Internet accelerator” and the accelerator acts as a proxy and has no “transparent proxy” feature and you need it. Globax was an example of alike accelerator, but Globax 5 has transparent proxy feature. That was the second redsocks` development goal;
  • you have to pass traffic through proxy due to corporate network limitation. That was never a goal for redsocks, but users have reported success with some proxy configurations.

以上是官方介绍。功能确实很强大,毕竟是俄国大佬的作品,继承了俄国产品强大而又粗糙的刻板印象。翻了他的repo,发现最后更新是2019年,而且还是改typo,其他的issue没有回复,大概大佬也不打算维护了吧。找了好久替代,发现了它的一个fork,似乎改了不少东西,就拖下来试试看吧。运行了一晚上,稳定性似乎还不错。

github地址 https://github.com/semigodking/redsocks

该repo依赖openssl和libevent2,libevent2在树莓派的repo中没有了,所以只能现编译。

现在把安装过程记录一下(仅限Raspberry Pi):

#安装openssl开发包

sudo apt install libssl-dev

#获取代码

wget https://github.com/libevent/libevent/releases/download/release-2.1.12-stable/libevent-2.1.12-stable.tar.gz

tar xvfz libevent-2.1.12-stable.tar.gz

cd libevent-2.1.12-stable

编译、安装

make
sudo make install

#克隆redsocks2的代码

git clone https://github.com/semigodking/redsocks

cd redsocks

make DISABLE_SHADOWSOCKS=1 ENABLE_STATIC=1

编译完,修改之前redsocks的配置文件、替换redsocks文件,重启服务即可。

目前暂时稳定。

訓練diffusion模型

訓練擴散模型


無條件圖像生成是擴散模型的一種流行應用,它生成的圖像與用於訓練的數據集中的圖像相似。通常,最好的結果是通過在特定數據集上微調預訓練模型來獲得的。您可以在 Hub 上找到許多這樣的檢查點,但如果您找不到您喜歡的檢查點,您可以隨時訓練自己的檢查點!

本教程將教您如何在 Smithsonian Butterflies 數據集的子集上從頭開始訓練 UNet2DModel,以生成您自己的 🦋 蝴蝶 🦋。

💡 本培訓教程基於 🧨 擴散器筆記本進行培訓。有關擴散模型的更多詳細信息和背景(例如它們的工作原理),請查看筆記本!

在開始之前,請確保您安裝了🤗數據集來加載和預處理圖像數據集,並安裝了🤗加速,以簡化在任意數量的 GPU 上的訓練。以下命令還將安裝 TensorBoard 以可視化訓練指標(您還可以使用權重和偏差來跟踪您的訓練)。

我們鼓勵您與社區分享您的模型,為此,您需要登錄您的 Hugging Face 帳戶(如果您還沒有帳戶,請在此處創建一個!)。您可以從筆記本登錄並在出現提示時輸入您的令牌:

Copied>>> from huggingface_hub import notebook_login >>> notebook_login()

或者從終端登錄:

Copiedhuggingface-cli login

由於模型檢查點非常大,因此安裝 Git-LFS 來對這些大文件進行版本控制:

Copied!sudo apt -qq install git-lfs !git config –global credential.helper store

訓練配置

為了方便起見,創建一個包含訓練超參數的 TrainingConfig 類(隨意調整它們):

Copied>>> from dataclasses import dataclass >>> @dataclass … class TrainingConfig: … image_size = 128 # the generated image resolution … train_batch_size = 16 … eval_batch_size = 16 # how many images to sample during evaluation … num_epochs = 50 … gradient_accumulation_steps = 1 … learning_rate = 1e-4 … lr_warmup_steps = 500 … save_image_epochs = 10 … save_model_epochs = 30 … mixed_precision = “fp16” # `no` for float32, `fp16` for automatic mixed precision … output_dir = “ddpm-butterflies-128” # the model name locally and on the HF Hub … push_to_hub = True # whether to upload the saved model to the HF Hub … hub_private_repo = False … overwrite_output_dir = True # overwrite the old model when re-running the notebook … seed = 0 >>> config = TrainingConfig()

加載數據集

您可以使用 🤗 數據集庫輕鬆加載史密森尼蝴蝶數據集:

Copied>>> from datasets import load_dataset >>> config.dataset_name = “huggan/smithsonian_butterflies_subset” >>> dataset = load_dataset(config.dataset_name, split=”train”)

💡 您可以從 HugGan 社區活動中找到其他數據集,也可以通過創建本地 ImageFolder 來使用自己的數據集。如果數據集來自 HugGan 社區活動,則將 config.dataset_name 設置為數據集的存儲庫 ID;如果您使用自己的圖像,則將 imagefolder 設置為。

🤗 Datasets 使用 Image 功能自動解碼圖像數據並將其加載為我們可以可視化的 PIL.Image :

Copied>>> import matplotlib.pyplot as plt >>> fig, axs = plt.subplots(1, 4, figsize=(16, 4)) >>> for i, image in enumerate(dataset[:4][“image”]): … axs[i].imshow(image) … axs[i].set_axis_off() >>> fig.show()

不過,這些圖像的尺寸各不相同,因此您需要先對它們進行預處理:

  • Resize 將圖像大小更改為 config.image_size 中定義的大小。
  • RandomHorizontalFlip 通過隨機鏡像圖像來擴充數據集。
  • Normalize 對於將像素值重新縮放到 [-1, 1] 範圍非常重要,這是模型所期望的。

Copied>>> from torchvision import transforms >>> preprocess = transforms.Compose( … [ … transforms.Resize((config.image_size, config.image_size)), … transforms.RandomHorizontalFlip(), … transforms.ToTensor(), … transforms.Normalize([0.5], [0.5]), … ] … )

使用 🤗 數據集的 set_transform 方法在訓練期間動態應用 preprocess 函數:

Copied>>> def transform(examples): … images = [preprocess(image.convert(“RGB”)) for image in examples[“image”]] … return {“images”: images} >>> dataset.set_transform(transform)

請隨意再次可視化圖像以確認它們已調整大小。現在您已準備好將數據集包裝在 DataLoader 中進行訓練!

Copied>>> import torch >>> train_dataloader = torch.utils.data.DataLoader(dataset, batch_size=config.train_batch_size, shuffle=True)

創建 UNet2D 模型

🧨 Diffusers 中的預訓練模型可以使用您想要的參數輕鬆地從其模型類創建。例如,要創建 UNet2DModel:

Copied>>> from diffusers import UNet2DModel >>> model = UNet2DModel( … sample_size=config.image_size, # the target image resolution … in_channels=3, # the number of input channels, 3 for RGB images … out_channels=3, # the number of output channels … layers_per_block=2, # how many ResNet layers to use per UNet block … block_out_channels=(128, 128, 256, 256, 512, 512), # the number of output channels for each UNet block … down_block_types=( … “DownBlock2D”, # a regular ResNet downsampling block … “DownBlock2D”, … “DownBlock2D”, … “DownBlock2D”, … “AttnDownBlock2D”, # a ResNet downsampling block with spatial self-attention … “DownBlock2D”, … ), … up_block_types=( … “UpBlock2D”, # a regular ResNet upsampling block … “AttnUpBlock2D”, # a ResNet upsampling block with spatial self-attention … “UpBlock2D”, … “UpBlock2D”, … “UpBlock2D”, … “UpBlock2D”, … ), … )

快速檢查樣本圖像形狀與模型輸出形狀是否匹配通常是個好主意:

Copied>>> sample_image = dataset[0][“images”].unsqueeze(0) >>> print(“Input shape:”, sample_image.shape) Input shape: torch.Size([1, 3, 128, 128]) >>> print(“Output shape:”, model(sample_image, timestep=0).sample.shape) Output shape: torch.Size([1, 3, 128, 128])

偉大的!接下來,您需要一個調度程序來向圖像添加一些噪聲。

創建調度程序

根據您使用模型進行訓練還是推理,調度程序的行為會有所不同。在推理過程中,調度器根據噪聲生成圖像。在訓練期間,調度程序從擴散過程中的特定點獲取模型輸出(或樣本),並根據噪聲調度和更新規則將噪聲應用於圖像。

讓我們看一下 DDPMScheduler,並使用 add_noise 方法向之前的 sample_image 添加一些隨機噪聲:

Copied>>> import torch >>> from PIL import Image >>> from diffusers import DDPMScheduler >>> noise_scheduler = DDPMScheduler(num_train_timesteps=1000) >>> noise = torch.randn(sample_image.shape) >>> timesteps = torch.LongTensor([50]) >>> noisy_image = noise_scheduler.add_noise(sample_image, noise, timesteps) >>> Image.fromarray(((noisy_image.permute(0, 2, 3, 1) + 1.0) * 127.5).type(torch.uint8).numpy()[0])

模型的訓練目標是預測添加到圖像中的噪聲。這一步的損失可以通過下式計算:

Copied>>> import torch.nn.functional as F >>> noise_pred = model(noisy_image, timesteps).sample >>> loss = F.mse_loss(noise_pred, noise)

 訓練模型

到目前為止,您已經掌握了開始訓練模型的大部分內容,剩下的就是將所有內容組合在一起。

首先,您需要一個優化器和一個學習率調度器:

Copied>>> from diffusers.optimization import get_cosine_schedule_with_warmup >>> optimizer = torch.optim.AdamW(model.parameters(), lr=config.learning_rate) >>> lr_scheduler = get_cosine_schedule_with_warmup( … optimizer=optimizer, … num_warmup_steps=config.lr_warmup_steps, … num_training_steps=(len(train_dataloader) * config.num_epochs), … )

然後,您需要一種評估模型的方法。為了進行評估,您可以使用 DDPMPipeline 生成一批樣本圖像並將其保存為網格:

Copied>>> from diffusers import DDPMPipeline >>> from diffusers.utils import make_image_grid >>> import math >>> import os >>> def evaluate(config, epoch, pipeline): … # Sample some images from random noise (this is the backward diffusion process).# The default pipeline output type is `List[PIL.Image]` … images = pipeline( … batch_size=config.eval_batch_size, … generator=torch.manual_seed(config.seed), … ).images … # Make a grid out of the images … image_grid = make_image_grid(images, rows=4, cols=4) … # Save the images … test_dir = os.path.join(config.output_dir, “samples”) … os.makedirs(test_dir, exist_ok=True) … image_grid.save(f”{test_dir}/{epoch:04d}.png”)

現在,您可以使用 🤗 Accelerate 將所有這些組件包裝在一個訓練循環中,以輕鬆進行 TensorBoard 日誌記錄、梯度累積和混合精度訓練。要將模型上傳到 Hub,請編寫一個函數來獲取存儲庫名稱和信息,然後將其推送到 Hub。

💡 下面的訓練循環可能看起來令人生畏且漫長,但是當您僅用一行代碼啟動訓練時,這一切都是值得的!如果您迫不及待地想要開始生成圖像,請隨意複製並運行下面的代碼。您可以隨時返回並更仔細地檢查訓練循環,例如當您等待模型完成訓練時。 🤗

Copied>>> from accelerate import Accelerator >>> from huggingface_hub import HfFolder, Repository, whoami >>> from tqdm.auto import tqdm >>> from pathlib import Path >>> import os >>> def get_full_repo_name(model_id: str, organization: str = None, token: str = None): … if token is None: … token = HfFolder.get_token() … if organization is None: … username = whoami(token)[“name”] … return f”{username}/{model_id}” … else: … return f”{organization}/{model_id}” >>> def train_loop(config, model, noise_scheduler, optimizer, train_dataloader, lr_scheduler): … # Initialize accelerator and tensorboard logging … accelerator = Accelerator( … mixed_precision=config.mixed_precision, … gradient_accumulation_steps=config.gradient_accumulation_steps, … log_with=”tensorboard”, … project_dir=os.path.join(config.output_dir, “logs”), … ) … if accelerator.is_main_process: … if config.push_to_hub: … repo_name = get_full_repo_name(Path(config.output_dir).name) … repo = Repository(config.output_dir, clone_from=repo_name) … elif config.output_dir is not None: … os.makedirs(config.output_dir, exist_ok=True) … accelerator.init_trackers(“train_example”) … # Prepare everything# There is no specific order to remember, you just need to unpack the# objects in the same order you gave them to the prepare method. … model, optimizer, train_dataloader, lr_scheduler = accelerator.prepare( … model, optimizer, train_dataloader, lr_scheduler … ) … global_step = 0 … # Now you train the model … for epoch in range(config.num_epochs): … progress_bar = tqdm(total=len(train_dataloader), disable=not accelerator.is_local_main_process) … progress_bar.set_description(f”Epoch {epoch}”) … for step, batch in enumerate(train_dataloader): … clean_images = batch[“images”] … # Sample noise to add to the images … noise = torch.randn(clean_images.shape).to(clean_images.device) … bs = clean_images.shape[0] … # Sample a random timestep for each image … timesteps = torch.randint( … 0, noise_scheduler.config.num_train_timesteps, (bs,), device=clean_images.device … ).long() … # Add noise to the clean images according to the noise magnitude at each timestep# (this is the forward diffusion process) … noisy_images = noise_scheduler.add_noise(clean_images, noise, timesteps) … with accelerator.accumulate(model): … # Predict the noise residual … noise_pred = model(noisy_images, timesteps, return_dict=False)[0] … loss = F.mse_loss(noise_pred, noise) … accelerator.backward(loss) … accelerator.clip_grad_norm_(model.parameters(), 1.0) … optimizer.step() … lr_scheduler.step() … optimizer.zero_grad() … progress_bar.update(1) … logs = {“loss”: loss.detach().item(), “lr”: lr_scheduler.get_last_lr()[0], “step”: global_step} … progress_bar.set_postfix(**logs) … accelerator.log(logs, step=global_step) … global_step += 1 … # After each epoch you optionally sample some demo images with evaluate() and save the model … if accelerator.is_main_process: … pipeline = DDPMPipeline(unet=accelerator.unwrap_model(model), scheduler=noise_scheduler) … if (epoch + 1) % config.save_image_epochs == 0 or epoch == config.num_epochs – 1: … evaluate(config, epoch, pipeline) … if (epoch + 1) % config.save_model_epochs == 0 or epoch == config.num_epochs – 1: … if config.push_to_hub: … repo.push_to_hub(commit_message=f”Epoch {epoch}”, blocking=True) … else: … pipeline.save_pretrained(config.output_dir)

唷,那是相當多的代碼!但您終於準備好使用 🤗 Accelerate 的 notebook_launcher 函數啟動訓練了。向函數傳遞訓練循環、所有訓練參數和進程數(您可以將此值更改為可用的 GPU 數量)以用於訓練:

Copied>>> from accelerate import notebook_launcher >>> args = (config, model, noise_scheduler, optimizer, train_dataloader, lr_scheduler) >>> notebook_launcher(train_loop, args, num_processes=1)

訓練完成後,查看擴散模型生成的最終 🦋 圖像 🦋!

Copied>>> import glob >>> sample_images = sorted(glob.glob(f”{config.output_dir}/samples/*.png”)) >>> Image.open(sample_images[-1])

下一步

無條件圖像生成是可訓練任務的一個示例。您可以訪問🧨 擴散器培訓示例頁面來探索其他任務和培訓技術。以下是您可以學到的一些示例:

  • 文本反轉,一種向模型傳授特定視覺概念並將其集成到生成圖像中的算法。
  • DreamBooth,一種在給定主題的多個輸入圖像的情況下生成主題的個性化圖像的技術。
  • 在您自己的數據集上微調穩定擴散模型的指南。
  • LoRA 使用指南,這是一種內存高效技術,可以更快地微調大型模型。

如何訓練穩定的擴散模型:初學者指南

掌握訓練穩定擴散模型的基礎知識。通過簡單的分步說明,我們將立即將初學者轉變為自信的模型訓練師。一起來學習吧!

近年來,人工智能 (AI) 和機器學習 (ML) 徹底改變了數據分析、預測建模和決策領域。其中一個發展就是穩定擴散模型,它是一種基於歷史數據生成圖像和預測結果的強大工具。

在這份內容廣泛的指南中,我們將深入研究穩定擴散模型的訓練過程,為您提供掌握這一迷人技術必不可少的步驟、最佳實踐和策略。

穩定擴散模型簡介

穩定擴散模型包含一種機器學習算法類型,該算法使用歷史數據來預測特定結果或事件的概率。這些模型採用一種稱為擴散過程的技術,其中包括向輸入圖像添加噪聲,然後隨著時間的推移逐漸減少噪聲以生成最終圖像。此過程產生的圖像比傳統深度學習 (DL) 模型更詳細、更真實。

穩定擴散模型因其處理複雜和抽象文本描述的能力而特別引人注目,這要歸功於一種稱為穩定訓練的新方法。該技術使模型能夠生成與文本輸入一致的高質量圖像,使其比以前的文本到圖像模型有了顯著改進。

穩定擴散模型的數據準備

在訓練穩定擴散模型之前,準備用於訓練模型的數據至關重要。該過程涉及以下步驟:

Data collection 數據採集

收集與您期望的結果相關的準確且最新的數據。確保數據準確地代表您希望模型解決的問題。

Data cleaning 數據清洗

消除數據集中的任何異常值、缺失數據或不一致之處,以最大限度地提高模型的準確性。這可能涉及填充缺失值、糾正錯誤或將數據轉換為更可用的格式。

Data preprocessing 數據預處理

應用各種技術來提高模型的準確性和性能。這可能包括歸一化、標準化或降維。

模型設計和算法選擇

準備好數據後,下一步就是設計穩定擴散模型。這涉及為模型選擇適當的算法、架構和參數。穩定擴散模型中使用的一些流行算法包括:

深度卷積神經網絡(DCNN)

生成對抗網絡(GAN)

變分自動編碼器(VAE)

選擇算法和架構時,請考慮問題的複雜性、數據集的大小以及所需的準確度等因素。

訓練穩定擴散模型

要訓​​練您自己的穩定擴散模型,您可以使用各種工具和平台,例如 Google Colab、Jupyter Notebooks 或 TensorFlow。這些平台提供了用於運行實驗、管理模型和生成圖像的交互式環境。

遵循本節中的步驟將使您能夠創建適合您的需求和偏好的擴散模型,從而產生富有洞察力的預測。以下是訓練穩定擴散模型的步驟:

將準備好的數據集分為訓練集和驗證集。使用訓練集來訓練模型,使用驗證集來評估其性能。

從各種可用選項中選擇合適的穩定擴散模型。

使用 PyTorch 或 TensorFlow 等軟件訓練模型。請注意,根據數據集大小和模型複雜性,訓練持續時間可以從幾個小時到幾天不等。

訓練後,使用驗證集評估模型的性能。

一旦對模型的性能感到滿意,就可以通過向模型提供隨機噪聲向量來生成圖像。

在我們詳細指南的幫助下輕鬆安裝穩定擴散。

模型評估和驗證

訓練穩定擴散模型後,必須評估其性能並驗證其準確性。這可以通過使用各種指標將模型的預測與實際結果進行比較來完成,例如:

均方誤差 (MSE)。

均方根誤差 (RMSE)。

平均絕對誤差 (MAE)。

R 平方(決定係數)。

此外,通過確保模型隨著時間的推移產生一致的結果來評估模型的穩定性也至關重要。

穩定擴散模型最佳實踐

為了確保穩定擴散模型的成功訓練,請考慮以下最佳實踐:

  • 使用準確、最新且具有代表性的數據進行培訓。
  • 使用不同的數據集測試模型以評估其性能。
  • 在評估過程中評估模型的準確性和穩定性。
  • 為模型選擇適當的算法、架構和參數。
  • 通過應用數據預處理技術來提高模型的準確性。
  • 並通過持續監控變更或更新來確保其持續性能。

擴散模型應用

擴散模型在各個行業都有廣泛的應用,包括金融、醫療保健、營銷和遊戲。穩定擴散模型的一些常見用途是:

  • 電子商務:網站可以使用穩定的擴散模型根據文字描述生成產品圖像,從而無需真實照片即可展示產品。
  • 廣告:廣告公司可以利用穩定的擴散模型為其廣告活動生成獨特且具有視覺吸引力的視覺效果。
  • 遊戲:遊戲開發者可以採用穩定的擴散模型,根據文本描述生成遊戲資產,例如角色和環境。

訓練穩定擴散需要多長時間?

訓練穩定擴散所需的時間取決於許多因素。因此包括數據集的大小、您使用的硬件以及輸出圖像的質量。

如果您使用較小的數據集或功能較弱的硬件,則訓練穩定擴散將需要更長的時間。例如,一位用戶報告說,他們花了大約 2 週的時間在單個 V100 GPU 上訓練穩定擴散。

如果您不確定訓練穩定擴散需要多長時間,您可以先嘗試使用較小的數據集或功能較弱的硬件。您還可以嘗試使用預先訓練的穩定擴散模型,這將為您節省一些時間。

以下是一些加快穩定擴散訓練的技巧:

  • 使用更大的數據集:更大的數據集將為穩定擴散提供更多可供學習的信息,這將加快訓練過程。
  • 使用更強大的GPU:更強大的GPU將能夠更快地處理數據,這也將加快訓練過程。
  • 使用預先訓練的模型:預先訓練的模型已經具有一些如何生成圖像的知識,這將加快訓練過程。

常見問題 (FAQ)

問:如何製作穩定擴散模型?

答:製作穩定擴散模型需要大量的技術知識和資源。這不是一件容易或快速就能完成的事情。以下是製作穩定擴散模型的基本步驟:

  • 收集大量圖像數據集:該數據集應盡可能多樣化,以確保模型可以生成各種圖像。
  • 預處理數據集:這涉及將圖像轉換為模型可以理解的格式。
  • 訓練模型:這是最耗時的步驟。該模型需要在數據集上進行長時間的訓練,才能學習如何生成圖像。
  • 測試模型:模型訓練完成後,需要在新的圖像數據集上進行測試。這將有助於確保模型生成高質量的圖像。

問:訓練穩定擴散模型需要多長時間?

答:訓練穩定擴散模型所需的時間取決於多種因素,包括數據集的大小、模型的複雜性和可用的計算資源。

一般來說,在大型數據集上訓練穩定擴散模型可能需要幾天甚至幾週的時間。例如,在 ImageNet 數據集上訓練穩定擴散模型在單個 GPU 上可能需要長達 10 天的時間。

如果您使用較小的數據集或不太複雜的模型,訓練時間會更短。然而,值得注意的是,即使是小模型也可能需要幾個小時來訓練。

問:如何通過穩定擴散獲得更好的結果?

答:您可以採取一些措施來獲得更好的穩定擴散效果:

  • 使用更大的數據集:模型需要學習的數據越多,結果就越好。
  • 使用更複雜的模型:更複雜的模型將能夠學習數據中更複雜的模式,從而獲得更好的結果。
  • 訓練模型的時間越長:模型訓練的時間越長,結果就越好。
  • 使用更好的優化器:優化器是一種幫助模型學習的數學算法。使用更好的優化器可以幫助模型更快收斂並獲得更好的結果。
  • 使用更複雜的損失函數:損失函數是一個數學方程,用於衡量模型的性能。使用更複雜的損失函數可以幫助模型更有效地學習。
  • 使用正則化技術:正則化是一種有助於防止模型過度擬合數據的技術。當模型學習數據中的噪聲而不是底層模式時,就會發生過度擬合。使用正則化技術有助於提高模型的泛化性能。

問:如何訓練穩定擴散權重?

用戶可以使用多種方法訓練穩定擴散權重,包括:

  • 監督學習:在監督學習中,模型在已標記有所需輸出的圖像數據集上進行訓練。例如,該模型可以使用貓和狗的圖像數據集,每個圖像標記為“貓”或“狗”。
  • 無監督學習:在無監督學習中,模型在沒有任何標籤的圖像數據集上進行訓練。該模型學習在數據中查找模式,而無需明確告知要查找什麼。
  • 半監督學習:在半監督學習中,模型在已部分標記的圖像數據集上進行訓練。當沒有足夠的標記數據可用時,這可能是提高模型性能的有用方法。

訓練穩定擴散權重的最佳方法取決於具體應用。例如,如果你想訓練一個模型來生成貓和狗的圖像,監督學習將是一個不錯的選擇。如果你想訓練一個模型來生成任何物體的圖像,無監督學習將是更好的選擇。

問:穩定擴散可以訓練嗎?

答:是的,穩定擴散是可以訓練的。它是一種擴散模型,是一類可以訓練生成圖像的生成模型。穩定擴散是一種特殊類型的擴散模型,其設計比其他擴散模型更穩定且更易於訓練。

穩定擴散模型使用稱為對抗訓練的技術進行訓練。在對抗性訓練中,兩個模型相互訓練。一種模型是生成器,它負責生成圖像。另一個模型是鑑別器,負責區分真實圖像和生成圖像。

生成器可以生成盡可能真實的圖像。鑑別器可以區分真實圖像和生成圖像。作為生成器和鑑別器,它們在各自的任務上變得更好。最終,生成器變得非常擅長生成逼真的圖像,以至於鑑別器無法再區分它們。

問:訓練穩定擴散需要多少張照片?

答:訓練穩定擴散所需的照片數量取決於要訓練的模型的大小和復雜性。較大的模型比較小的模型需要更多的照片。

一般來說,您至少需要幾千張照片來訓練穩定擴散模型。但是,如果使用稱為數據增強的技術,則可以使用更少的照片來訓練模型。數據增強是一種通過從現有圖像創建新圖像來人為地增加數據集大小的技術。

結論

訓練穩定的擴散模型似乎具有挑戰性。然而,理解這個過程並使用正確的方法可以將其變成結果預測的強大工具。

本綜合指南的步驟和最佳實踐可以幫助您掌握這些模型的訓練並充分發揮其潛力。

無論您是業餘愛好者還是專業人士,深入研究穩定擴散模型的令人興奮的世界都可以讓您創造出令人驚嘆的視覺效果並獲得寶貴的見解。

今夜は月が綺麗ですね

月が綺麗な夜に、突然意中の人に「今夜は月が綺麗ですね」と言われたら、どう感じますか。ロマンチックな人や、なんかキザったらしい人だと感じる人も多いでしょう。中には「今夜は月が綺麗ですね」の意味を知っていて、洒落たことを言い返したいと思う人もいるでしょう。

この「今夜は月が綺麗ですね」にはどんな意味を持っているのか、これを意味することを知れば、日本語の奥ゆかしさに歯がゆさを感じるでしょう。

夏目漱石说在日语里要表达我爱你,应说成 今夜月色真好。

那为什么月色又会成为我爱你的表达呢? 在日语中月读作つき(tsuki),恰好和喜欢的日语好き(すき,suki)读音相近,日本人把谐音哏玩得很溜啊。东方人含蓄蕴藉,隐忍克制,大多不会像西方人那样热烈奔放,直抒胸臆。从他们口中断然不会轻易吐露那三个字,他们只会借赋比兴来表达爱意。他们会说,“思君如满月,夜夜减清辉。”他们会说,“此时相望不相闻,愿逐月华流照君。”他们会说,“愿我如星君如月,夜夜流光相皎洁。”他们会说,“月が绮丽ですね。”

優やさしい詩

やさしいうた – RSP

  • ひろ世界せかいでたったひとつの
  • 在這廣闊世界僅有的一首
  • ちいさなあたしのねがうた
  • 屬於我小小的祈願詩歌
  • わらってしいどこかとおくの
  • 想要微笑好像又漸行漸遠
  • 名前なまえらないあなたにも
  • 連你的名字都不曾知曉
  • うまくえたらつらくならない
  • 能好好表達的話就不會心酸不已
  • うまくけたならくるしくもない
  • 能好好哭出來的話就不會飽受痛苦
  • うつむいたままそんなあなたに やすらぎのひとつあたえられたら…
  • 低下頭 若是能授予這樣的你一份安逸的話…
  • そらはいつでもだれうえでも
  • 就算天空總是一直在我們上方
  • 青色あおいろしてた見上みあげてごらん
  • 抬頭觀賞那抹蔚藍色彩
  • やさしいうたうたっていたい
  • 想要歌唱那首温柔的歌謠
  • よわひとにもつよひとにも
  • 不管是對柔弱的人 還是對堅强的人
  • うそやいいわけこころかくして
  • 謊言或藉口隐藏心中
  • 傷付きずつくことをずっとけていた
  • 一直都避免受傷
  • となりすわるほんとの自分じぶん
  • 一旁坐著真正的自己
  • あなたはそれに気付きづかなくて
  • 而你也没有發現到這一點
  • ひといたみをかんじたとき
  • 當感覺到為人之痛的時候
  • しあわせなひとつけたとき
  • 當發現幸福的人的時候
  • おな気持きもちでなみだしたのも
  • 也都帶著同樣地心情落下淚水
  • あなたのこころわすれないよう
  • 也不能忘却你的真心
  • そらはいつでもだれうえでも
  • 就算天空總是一直在我們上方
  • 青色あおいろしてた見上みあげてごらん
  • 抬頭觀賞那抹蔚藍色彩
  • やさしいうたうたっていたい
  • 想要歌唱那首温柔的歌謠
  • よわひとにもつよひとにも
  • 不管是對柔弱的人 還是對堅强的人
  • そらはいつでもだれうえでも
  • 就算天空總是一直在我們上方
  • 青色あおいろしてたそれがうれしい
  • 那抹蔚藍讓人歡心喜悦
  • やさしいうたうたっていたい
  • 想要歌唱那首温柔的歌謠
  • よわひとにもつよひとにも
  • 不管是對柔弱的人 還是對堅强的人
  • どこでまれて どんないろって
  • 在某處被降生下來 不管帶著什麼色彩
  • たったひとりのあなたいるから
  • 你是只此唯一的存在
  • 今日きょうきてる ひとがいること
  • 今天也安然活著 生命的存在
  • わすれないでよ つよいあなたへ
  • 别忘記了哦 獻給堅强的你

歌词来源:http://blog.xuite.net/l42934293/blog/213750290 及 https://www.jpmarumaru.com/tw/JPSongPlay-2781.html

距离上一篇博文已经3年多了,一直没有静下心来写点东西的机会。

上一篇博文还在而立,这篇已经到了不惑了。终于还是活成了自己讨厌的人。在年轻的时候觉得以自己方式活着就好,到了临近中年才发现,终究还是要碰壁的,这确实够讽刺的。

但是生活还是要继续,面对现实,一生都要抱着学习的态度提高自己,保持自己的竞争力。

以太坊入门(三)用web3j进行以太转账及代币转账

上章讲到账户的查询,本章讲述账户转账。

  1. 以太坊转账 We3j web3j = Web3j.build(new HttpService(ConstantLibs.WEB3_ADDRESS)); Credentials credentials = WalletTool.loadCredentials(fromAddress); EthGetTransactionCount ethGetTransactionCount = web3j.ethGetTransactionCount( fromAddress, DefaultBlockParameterName.LATEST).sendAsync().get(); BigInteger nonce = ethGetTransactionCount.getTransactionCount(); RawTransaction rawTransaction = RawTransaction.createEtherTransaction( nonce, Convert.toWei("18", Convert.Unit.GWEI).toBigInteger(), Convert.toWei("45000", Convert.Unit.WEI).toBigInteger(), toAddress, new BigInteger(amount)); byte[] signedMessage = TransactionEncoder.signMessage(rawTransaction, credentials); String hexValue = Numeric.toHexString(signedMessage); EthSendTransaction ethSendTransaction = web3.ethSendRawTransaction(hexValue).sendAsync().get(); if (ethSendTransaction.hasError()) { log.info("transfer error:", ethSendTransaction.getError().getMessage()); } else { String transactionHash = ethSendTransaction.getTransactionHash(); log.info("Transfer transactionHash:" + transactionHash); }
  2. 以太坊代币转账 Web3j web3j = Web3j.build(new HttpService(ConstantLibs.WEB3_ADDRESS)); Credentials credentials = WalletTool.loadCredentials(fromAddress); EthGetTransactionCount ethGetTransactionCount = web3j.ethGetTransactionCount( fromAddress, DefaultBlockParameterName.LATEST).sendAsync().get(); BigInteger nonce = ethGetTransactionCount.getTransactionCount(); Function function = new Function( "transfer", Arrays.asList(new Address(toAddress), new Uint256(new BigInteger(amount))), Arrays.asList(new TypeReference<Type>() { })); String encodedFunction = FunctionEncoder.encode(function); RawTransaction rawTransaction = RawTransaction.createTransaction(nonce, Convert.toWei("18", Convert.Unit.GWEI).toBigInteger(), Convert.toWei("100000", Convert.Unit.WEI).toBigInteger(), contractAddress, encodedFunction); byte[] signedMessage = TransactionEncoder.signMessage(rawTransaction, credentials); String hexValue = Numeric.toHexString(signedMessage); log.debug("transfer hexValue:" + hexValue); EthSendTransaction ethSendTransaction = web3.ethSendRawTransaction(hexValue).sendAsync().get(); if (ethSendTransaction.hasError()) { log.info("transfer error:", ethSendTransaction.getError().getMessage()); } else { String transactionHash = ethSendTransaction.getTransactionHash(); log.info("Transfer transactionHash:" + transactionHash); }

代币转账和以太转账的区别在于,to地址是合约地址,而input是有三部分数据构成:transfer方法的哈希+收款人的地址+转账金额。此处比较难理解的正是Function部分,设置好参数以后,调用rawTransaction就可以了。

本站始发,转载于 https://www.jianshu.com/p/8ae984e6bafc

以太坊入门(二)用web3j进行以太查询及通证查询

以太坊的开发,基本都是go语言和nodejs的天下,web3j出现给java开发人员提供了很大的便利。本文会对一些以太坊的基本操作用java语言来实现。

本章会讲述通过web3j进行账户余额的查询。

  1. 以太余额查询
    以太的余额查询比较简单,直接调用web3j的ethGetBalance就可以。 Web3j web3j = Web3j.build(new HttpService(ConstantLibs.WEB3_ADDRESS)); EthGetBalance ethGetBalance = web3j.ethGetBalance( address, DefaultBlockParameterName.LATEST).sendAsync().get(); BigInteger balance = ethGetBalance.getBalance();
  2. 通证的余额查询
    代币的查询就比较复杂一些,研究了好长时间,最后发现每个代币合约都会实现balanceOf方法,可以通过这个方法来查询通证的余额。 Web3j web3j = Web3j.build(new HttpService(ConstantLibs.WEB3_ADDRESS)); Function function = new Function( "balanceOf", Arrays.asList(new Address(address)), // Solidity Types in smart contract functions Arrays.asList(new TypeReference<Type>() { })); String encodedFunction = FunctionEncoder.encode(function); org.web3j.protocol.core.methods.response.EthCall response = web3j.ethCall( org.web3j.protocol.core.methods.request.Transaction.createEthCallTransaction(address, contract, encodedFunction), DefaultBlockParameterName.LATEST) .sendAsync().get(); String returnValue = response.getValue(); //返回16进制余额 returnValue = returnValue.substring(2); BigInteger balance = new BigInteger(returnValue, 16);

本站始发,转载于https://www.jianshu.com/p/0f3e65622d65

以太坊入门(一)账户和nonce的关系

什么是nonce?

在以太坊的交易数据中,大家都可以看到一个数字,nonce,从0开始,一直向上递增,这个代表什么意思呢?

以太坊所有的交易都是基于account的,不同于基于utxo的比特币,因此需要对每次交易都按顺序记录,nonce值就是这个顺序,主要用来防止重放攻击。

发起一笔交易,nonce就会加一。对于发起的解释:

1.外部账户每发送一笔交易;

2.合约账户每创建一个合约

而转入交易、合约调用其他合约等属于内部调用,因此nonce值不变。

如何使用nonce

发起转账或者创建合约的时候,通过web3从以太坊网络查询当前的nonce(ethGetTransactionCount)值,使用此值作为当前交易的nonce值,发送到以太坊网络即可。

nonce使用的几条规则

1. 当nonce太小(小于当前的nonce值),交易会被直接拒绝,Transactions with too low a nonce get immediately rejected;

2. 当nonce太大,大于当前nonce,交易会一直处于队列之中,Transactions with too high a nonce get placed in the transaction pool queue;

3.当发送一个比较大的nonce值,然后补齐开始nonce到那个值之间的nonce,那么交易依旧可以被执行,If transactions with nonces that fill the gap between the last valid nonce and the too high nonce are sent and the nonce sequence is complete, all the transactions in the sequence will get processed and mined.

4. 交易队列只保存最多64个从同一个账户发出的交易,The transaction pool queue will only hold a maximum of 64 transactions with the same From:address with nonces out of sequence. 也就是说,如果要批量转账,同一节点不要发出超过64笔交易。

5.当某节点queue中还有交易,但此时停止geth客户端,queue中的交易会被清除掉,When the geth instances are shut down and restarted, transactions in the transaction pool queue disappear.

6.当前nonce合适,但是账户余额不足时,会被以太坊拒绝;

7.如果发起一笔交易,但是因为gwei比较低或者网络比较忙的时候,该交易还没矿工挖出,可以通过使用相同的nonce和较高的gas费用,从而“覆盖”前一笔交易;

树莓派充当airplay接收器

iOS升级以后,新版本经常不兼容xbian之类自带的airplay功能,目前来Shairport的支持还是不错的。我在新的树梅派3b+上编译了一下,支持ios 11.0.4版本。

首先升级树莓派

sudo apt-get update
sudo apt-get upgrade

然后安装依赖包

sudo apt-get install autoconf automake avahi-daemon build-essential git libasound2-dev libavahi-client-dev libconfig-dev libdaemon-dev libpopt-dev libssl-dev libtool xmltoman

下载shairport源码

git clone https://github.com/mikebrady/shairport-sync.git

生成配置文件

cd shairport-sync
autoreconf -i -f
./configure --with-alsa --with-avahi --with-ssl=openssl --with-systemd --with-metadata

编译安装

make
sudo make install

设置为自动启动

sudo systemctl enable shairport-sync

启动

sudo service shairport-sync start

然后就可以在手机里面找到airplay设备,raspberrypi了。

Nginx反向代理跨域option问题解决

CORS on Nginx

The following Nginx configuration enables CORS, with support for preflight requests.

#
# Wide-open CORS config for nginx
#
location / {
     if ($request_method = 'OPTIONS') {
        add_header 'Access-Control-Allow-Origin' '*';
        add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
        #
        # Custom headers and headers various browsers *should* be OK with but aren't
        #
        add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range';
        #
        # Tell client that this pre-flight info is valid for 20 days
        #
        add_header 'Access-Control-Max-Age' 1728000;
        add_header 'Content-Type' 'text/plain; charset=utf-8';
        add_header 'Content-Length' 0;
        return 204;
     }
     if ($request_method = 'POST') {
        add_header 'Access-Control-Allow-Origin' '*';
        add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
        add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range';
        add_header 'Access-Control-Expose-Headers' 'Content-Length,Content-Range';
     }
     if ($request_method = 'GET') {
        add_header 'Access-Control-Allow-Origin' '*';
        add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
        add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range';
        add_header 'Access-Control-Expose-Headers' 'Content-Length,Content-Range';
     }
}

First is tricking Nginx that a 405 status is actually a 200 OK and then proxy_pass it to your HAProxy like this:

error_page 405 =200 @405;
location @405 {
    root /;
    proxy_pass http://yourproxy:8080;
}

The second solution is just to catch the OPTIONS request and build a response for those requests:

location / {
    if ($request_method = OPTIONS ) {
        add_header Content-Length 0;
        add_header Content-Type text/plain;
        return 200;
    }
}

春风十里

春风十里
作曲作词 倍倍

我在二环的里边 想着你
你在远方的山上 春风十里
今天的风吹向你 下了雨
我说所有的酒 都不如你

我在鼓楼的夜色中 为你唱花香自来
在别处 沉默相遇和期待
飞机飞过 车水马龙的城市
千里之外 不离开

把所有的春天 都揉进了一个清晨
把所有停不下来的言语变成秘密 关上了门
莫名的情愫啊 请问 谁来将它带走呢
只好把岁月化成歌 留在山河

时间过得很快,2017年就这么过去了。

这两天一直在循环播放《春风十里》这首歌。最喜欢的一句就是:我说所有的酒,都不如你,千里之外 不离开。

酒,对于每个人来说,都是不同的东西,有喜、有恶。正如小站的名字,酒趣琴音,达是酒中趣,琴上偶然音。前两天朋友圈有人晒出在米国时候的照片,一想已经毕业了七年。三五好友凑一起,小酌,谈谈古,论论今,甚是怀念。当年春田还是留下了太多的身影,十一叔,River,Candy,十三姨,老姑…回国以后,各奔东西,即使是同一城市,也难得碰一次。虽然联系很少,偶尔在异地出差,聚在一起,总还是有说不完的话题。从城市各个角落聚拢,陪吃,陪喝,陪聊,还有陪压马路,当今的快节奏,每个人都很忙,花时间陪伴,也只有真切的同学之间才可能做到了。鸿鹄也好,鹡鸰也好,缘分让大家聚在春田,也让大家留下了这份纯真。

对我来说2017,算是比较动荡的一年,在某个字母公司待了8年,终于下定决心离开了,跑到另外一座城市,迎接未知的挑战。虽然和来之前的初衷有很大的偏差,人性好的、坏的都见识和领教过,至少不算好也不算坏,这一年就这么过去了。生活不只有眼前苟且,还有诗远方。要用眼睛看到诗和远方,还要尝试把诗与远方变成生活,2018谈谈理想吧。

所有的酒,都不如你;千里之外,不曾离开。

[转]古诗中的”斜“究竟怎么读

《小学语文教师》2000年第5期上刊有胡孟铭老师的文章《“斜”在〈山行〉中还是读xia好》,主张将“斜”字还其旧读、读古音xia,理由是:“既然课题为古诗,就应读古韵,取古义,才能字义准确,和谐动听”。本刊2000年第10期上又刊有史南先生的文章《“斜”不可读xia》,予以反对,理由是:1985年国家公布的《普通话异读词审音表》“不管那些当时不存在异读的词;如‘斜’字在普通话字(词)典上早已取消了古读音,因此在《审音表》中没有收列它,表明它除了读xie之外,普通话中不存在别的读音”“小学语文教学大纲要求小学生能用普通话朗读课文……再坚持读古音,就违背了国家有关语言文字的政策法规”。

问题至此似乎已有了答案。但笔者的看法却与以上两位老师皆有所不同。语言文字的使用,的确如史老师所说,应当以国家的政策法规为依据。而最高的依据则是2000年10月31日九届人大常委会通过并颁布的《中华人民共和国国家通用语言文字法》(以下简称《语言文字法》)。两位老师讨论时,本法尚未公布。但本法自2001年1月1日实施以后,“斜”的读音问题理当有新的答案。

“斜”字确实存在着旧读音xia,后来演变出xie音,但意义没变(胡老师所谓“‘斜’的读音发生了变化,词义的范围也大大缩小了。倘若将‘斜’还其旧韵xia,(则有)古义‘倾侧或曲折向前延伸’”是无根据的,《康熙字典》《辞源》等古今字书辞书足以为训)。但在古诗教学中究竟应读古音xia,还是今音xie?则应视情形而论。
《语言文字法》规定:“本法所称的国家通用语言文字是普通话和规范汉字”,我们在教学中就应当以国家通用语言文字为准,这是毫无疑义的。所以,教材在古诗《山行》中将“斜”注音为xie是正确的,不但如此,“斜”在本篇课文中还是个生字,教师在教学“斜”这个单字时,也应当训为xie。胡老师对这一点提出异议,笔者不能苟同。但在读本诗的时候也读为xie,又确实拗口。本诗的“斜”字出现在首句,似乎还可将就一二,很多唐诗中“斜”字都出现在偶句末尾。我们知道,古诗的平仄、对仗、押韵,以押韵为第一,当时这些诗都是严格押韵的。若诵读这些诗时也读该字为xie,古诗固有的韵律美被破坏,更让人难于接受。如:
刘禹锡《乌衣巷》
朱雀桥边野草花,乌衣巷口夕阳斜。旧时王谢堂前燕,飞入寻常百姓家。
刘方平《夜月》
更深月色半人家,北斗阑干南斗斜。今夜偏知春气暖,虫声新透绿窗纱。
张泌《寄人》
别梦依依到谢家,小廊回合曲阑斜。多情只有春庭月,犹为离人照落花。
韩君平《寒食》
春城无处不飞花,寒食东风御柳斜。日暮汉宫传蜡烛,轻烟散入五侯家。
元稹《菊花》
秋丛绕舍似陶家,遍绕篱边日渐斜。不是花中偏爱菊,此花开尽更无花。
皎然《寻陆鸿渐不遇》
移家虽带郭,野径入桑麻。近种篱边菊,秋来未着花。扣门无犬吠,,欲去问西家。报道山中去,归来每日斜。
张若虚《春江花月夜》(节录)
昨夜闲潭梦落花,可怜春半不还家。江水流春去欲尽,江潭落月复西斜。
张祜《吴兴新堤》(节录)
春堤一望思无涯,树势还同水势斜。深映菰蒲三十里,晴分功利几千家。

史老师在文章最后说:“但为了让学生知道原来是押韵的,在教学时不妨告诉学生‘斜’在古代的韵母就读ia,直到今天,吴方言中还保留它的古读音,读zia”。笔者认为这样还不够,在诵读这些古诗时就应当读‘斜’为xia。因为教学语言文字与欣赏文学艺术是两回事。我们教学古诗时,这两种活动是同时存在的,但也是完全能够而且应该把二者区分开来的。有的老师就是这样教的:领着学生读“远上寒山石径斜xia……”,然后跟学生讲:“‘斜’字课本上注音为xie,就是‘歪斜’的意思,平时就应当读xie,为什么在诗里读xia呢?这是因为这个字在以前的韵母就是ia,这首诗当时是很押韵的。我们读诗时,为了体会诗的韵律美,也为了更上口,就暂时读xia”。可以确信,只要老师教得明白,学生是不会在其他场合、在说话和写作应用中也读为xia的。这样,不但不会影响到语言文字规范化纯洁化的问题,相反学生还会多学到一点知识,知道“斜”字在古音韵母读ia,知道古人作诗是很重押韵的,更能体会到古诗的韵律美,更容易体会到前人的诗歌艺术才华和成就。岂不更好?重要的是,这样做也有法律的依据。《语言文字法》明确规定:“有下列情形的,可以使用方言:……三戏曲影视等艺术形式中需要使用的;四出版、教学、研究中确需使用的”。这里所谓的“方言”,当然也包括像“斜xia”这样的以方言形式存在的古音韵母。

当然,这种“暂时地读为古音”也是有条件的:必须是“斜”字作韵脚,在韵律的“胁迫”下自然而然地读为xia;如果“斜”虽在古诗中,但不作韵脚,则须还其正读xie音。

对于这种处理,我们还可从对古字形的处理当中找到类似的旁证。比如胡、史二位老师讨论的“鹿柴”。“鹿柴”中“柴”之所以读zhai,是因为“柴”是“寨”的通假字。但是在现代汉语用字标准中,“寨”是不能再写作“柴”了。课本之所以仍保留原来的字形“柴”,无非也是让学生多一点知识,知道“寨”在古代可通写作“柴”,为以后直接阅读古书提供方便而已。《语言文字法》也有相应的规定:“有下列情形的,可以保留或使用繁体字、异体字:……三书法、篆刻等艺术作品;……五出版、教学、研究中需要使用的”。
至于汉匈奴单于“伊稚斜”仍读cha,不读xie,则是因为《审音表》“对大量的人名、地名中的异读字未加审订,这些人名、地名中的异读字照样异读”。

在欣赏性的唐诗吟诵中,为求合于韵律、为求顺口而需要暂时恢复原来读音的字,还有很多。除“斜”字外,另一个比较常见的是“回”字。古诗中“回”作韵脚时,应读古音huai,而不读hui。如:
李白《望天门山》
天门中断楚江开,碧水东流至此回。两岸青山相对出,孤帆一片日边来。
杜甫《登高》(节录)
风急天高猿啸哀,渚清沙白鸟飞回。无边落木潇潇下,不尽长江滚滚来。
刘禹锡《戏赠看花诸君子》
紫陌红尘拂面来,无人不道看花回。玄都观里桃千树,尽是刘郎去后栽。
刘禹锡《石头城》
山围故国周遭在,潮打空城寂寞回。淮水东边旧时月,夜深还过女墙来。
杜牧《早雁》
金河秋半虏弦开,云外惊飞四散哀。仙掌月明孤影过,长门灯暗数声来。须知胡骑纷纷在,岂逐春风一一回。莫厌潇湘少人处,水多菰米岸莓苔。

“回”在唐诗韵脚中读huai,当无疑义。不过这又牵涉到另一个“衰”的读音。《长歌行》有“阳春布德泽,万物生光辉。长恐秋节至,焜黄华叶衰。百川东到海,何时复西归。”;贺知章《回乡偶书》有“少小离家老大回,乡音无改鬓毛衰。儿童相见不相识,笑问客从何处来。”--“衰”当读何音?历来说法不一:或有前首训为shuai的,或有后首训为cui的。笔者的意见则是:“衰”在前诗中训为cui,或有后诗中训为shuai。因为:“衰”字,作“形容萎缩、体量递减”时读cui,如“等衰”;作“气色衰老”时读shuai,如“衰草”。因此,“焜黄华叶衰”中读cui,是指鲜亮的花和叶子萎缩脱落、是形体之变;而“鬓毛衰”中读shuai,是两鬓斑白之意,是气色之变,不是形体之变,不是鬓毛萎缩脱落之意(因为人的鬓毛是头发中最不易脱落的)。诗中的韵脚“辉、归”“回、来”等字可作旁证。

CentOS 7 下 firewalld的基本操作

这两天服务器升级,升级后发现有些服务不能用了。后来研究了一下,是因为防火墙的原因。之前调试,为了偷懒,把防火墙关了,就没启动过,现在想想裸奔了几个月,心真是大。

对外增加服务:

firewall-cmd --zone=public --add-port=80/tcp --permanent

对内设置安全区

firewall-cmd –permanent –zone=internal –change-interface=enp03s

firewall-cmd --zone=public --add-port=3306/tcp --permanent

最后,再重新加载firwalld策略

firewall-cmd --reload

以下转载:

http://qianxunclub.com/linux-centos-7-fang-huo-qiang-zhi-ju-you-ming-ling-xing-de-firewalldde-ji-ben-cao-zuo/

启动FirewallD服务:

?

1
2
systemctl enable firewalld.service #设置开机启动
systemctl start firewalld.service #开启服务

查看防火墙状态:

?

1
systemctl status firewalld

1. 区域管理

1.1. 网络区域简介

通过将网络划分成不同的区域,制定出不同区域之间的访问控制策略来控制不同程序区域间传送的数据流。例如,互联网是不可信任的区域,而内部网络是高度信任的区域。网络安全模型可以在安装,初次启动和首次建立网络连接时选择初始化。该模型描述了主机所连接的整个网络环境的可信级别,并定义了新连接的处理方式。有如下几种不同的初始化区域:

  • 阻塞区域(block):任何传入的网络数据包都将被阻止。
  • 工作区域(work):相信网络上的其他计算机,不会损害你的计算机。
  • 家庭区域(home):相信网络上的其他计算机,不会损害你的计算机。
  • 公共区域(public):不相信网络上的任何计算机,只有选择接受传入的网络连接。
  • 隔离区域(DMZ):隔离区域也称为非军事区域,内外网络之间增加的一层网络,起到缓冲作用。对于隔离区域,只有选择接受传入的网络连接。
  • 信任区域(trusted):所有的网络连接都可以接受。
  • 丢弃区域(drop):任何传入的网络连接都被拒绝。
  • 内部区域(internal):信任网络上的其他计算机,不会损害你的计算机。只有选择接受传入的网络连接。
  • 外部区域(external):不相信网络上的其他计算机,不会损害你的计算机。只有选择接受传入的网络连接。

注:FirewallD的默认区域是public。

1.2. 显示支持的区域列表

?

1
firewall-cmd --get-zones

1.3. 设置为家庭区域

?

1
firewall-cmd --set-default-zone=home

1.4. 查看当前区域

?

1
firewall-cmd --get-active-zones

1.5. 设置当前区域的接口

?

1
firewall-cmd --get-zone-of-interface=enp03s

1.6. 显示所有公共区域(public)

?

1
firewall-cmd --zone=public --list-all

1.7. 临时修改网络接口(enp0s3)为内部区域(internal)

?

1
firewall-cmd --zone=internal --change-interface=enp03s

1.8. 永久修改网络接口enp03s为内部区域(internal)

?

1
firewall-cmd --permanent --zone=internal --change-interface=enp03s

2. 服务管理

2.1. 显示服务列表

Amanda, FTP, Samba和TFTP等最重要的服务已经被FirewallD提供相应的服务,可以使用如下命令查看:

?

1
firewall-cmd --get-services

2.2. 允许SSH服务通过

?

1
firewall-cmd --enable service=ssh

2.3. 禁止SSH服务通过

?

1
firewall-cmd --disable service=ssh

2.4. 打开TCP的8080端口

?

1
firewall-cmd --enable ports=8080/tcp

2.5. 临时允许Samba服务通过600秒

?

1
firewall-cmd --enable service=samba --timeout=600

2.6. 显示当前服务

?

1
firewall-cmd --list-services

2.7. 添加HTTP服务到内部区域(internal)

?

1
2
firewall-cmd --permanent --zone=internal --add-service=http
firewall-cmd --reload #在不改变状态的条件下重新加载防火墙

3. 端口管理

3.1. 打开端口

?

1
2
#打开443/TCP端口
firewall-cmd --add-port=443/tcp

?

1
2
3
4
5
#永久打开3690/TCP端口
firewall-cmd --permanent --add-port=3690/tcp
#永久打开端口好像需要reload一下,临时打开好像不用,如果用了reload临时打开的端口就失效了
#其它服务也可能是这样的,这个没有测试
firewall-cmd --reload

?

1
2
#查看防火墙,添加的端口也可以看到
firewall-cmd --list-all

4. 直接模式

FirewallD包括一种直接模式,使用它可以完成一些工作,例如打开TCP协议的9999端口

?

1
2
firewall-cmd --direct -add-rule ipv4 filter INPUT 0 -p tcp --dport 9000 -j ACCEPT
firewall-cmd --reload

5. 关闭服务的方法

你也可以关闭目前还不熟悉的FirewallD防火墙,而使用iptables,命令如下:

?

1
2
3
4
5
systemctl stop firewalld
systemctl disable firewalld
yum install iptables-services
systemctl start iptables
systemctl enable iptables