分类: 站长笔记

虚拟人生记录,站长思维沉淀

  • 评估自动文摘质量的终极指南

    你是否曾经尝试过创建自动文摘系统,但却不知道如何准确评估其质量?本文将为您提供关于如何评估抽象文摘质量的详细指南。我们将介绍传统的评估方法,如ROUGE和BERTScore,以及一种创新的方法,利用大型语言模型(LLM)作为评估器。

    1. 开篇故事

    在探讨自动文摘质量评估之前,让我们来看一个生动有趣的故事。假设你是一名AI研究员,正在开发一款自动文摘系统,这个系统可以将长篇文章精炼成简短的摘要。你花费了数月时间不断改进算法,优化性能,但你开始面临一个棘手的问题:如何准确地评估你的文摘质量?这个问题在自然语言处理领域一直备受关注,因为它涉及到了如何衡量文本的准确性、连贯性和相关性。

    在这个教程中,我们将为你揭示评估自动文摘质量的关键方法,让你的工作更具实际应用性和可衡量性。

    2. 引言

    自动文摘质量评估是一个耗时的过程,因为它涉及到不同的质量指标,如连贯性、简洁性、可读性和内容。传统的自动评估指标,如ROUGE和BERTScore等,虽然具体可靠,但可能与实际文摘质量的相关性不高。特别是对于开放性生成任务,它们与人类评价的相关性相对较低。因此,在评估文摘质量时,越来越需要依赖人类评估、用户反馈或基于模型的指标,同时需要警惕潜在的偏见。虽然人类判断提供了宝贵的见解,但通常不具备可扩展性,并且成本较高。

    除了传统的评估指标,我们还展示了一种使用大型语言模型(LLM)的方法(G-Eval),用作评估抽象性摘要的新型、无参考度量。在这种情况下,我们使用gpt-4来评分候选输出。gpt-4已经有效地学习了语言质量的内部模型,使其能够区分流畅、连贯的文本和低质量的文本。利用这种内部评分机制,可以自动评估LLM生成的新候选输出。

    3. 安装必要的软件包

    在进行评估之前,我们需要安装一些必要的软件包,以便进行评估。这些包包括ROUGE、BERTScore和OpenAI的API。

    !pip install rouge --quiet
    !pip install bert_score --quiet
    !pip install openai --quiet
    import openai
    import os
    import re
    import pandas as pd
    
    # 这里放置Python实现ROUGE指标的代码
    from rouge import Rouge
    
    # BERTScore利用BERT的上下文嵌入并通过余弦相似度匹配候选和参考句子中的单词。
    from bert_score import BERTScorer
    
    openai.api_key = os.environ.get("OPENAI_API_KEY")

    4. 示例任务

    为了本教程的目的,我们将使用以下示例文摘任务。请注意,我们提供了两个生成的摘要供比较,以及一个参考的人工撰写摘要,这是ROUGE和BERTScore等评估指标所需的。

    摘录 (excerpt)

    OpenAI的使命是确保人工通用智能(AGI)造福全人类。OpenAI将直接构建安全和有益的AGI,但如果其工作有助于其他人实现这一目标,它也将认为使命已达成。OpenAI遵循几个关键原则。首先,广泛分布的利益 – 对AGI部署的任何影响都将用于全人类的利益,以避免有害的使用或权力过度集中。其次,长期安全 – OpenAI致力于进行研究,使AGI安全,并促进这些研究在AI社区中的采纳。第三,技术领导 – OpenAI旨在处于AI能力的前沿。第四,合作导向 – OpenAI积极与其他研究和政策机构合作,并寻求创建一个共同工作的全球社区,共同解决AGI的全球挑战。

    摘要 (Summaries):

    • 参考摘要 /ref_summary(人工生成)
      OpenAI的目标是确保人工通用智能(AGI)造福所有人,避免有害用途或权力过度集中。它致力于研究AGI的安全性,并在AI社区中促进这些研究。OpenAI旨在领导AI能力的发展,与全球研究和政策机构合作,解决AGI的挑战。

    • 评估摘要1 / eval_summary_1(系统生成)
      OpenAI旨在使AGI造福全人类,避免有

    害用途和权力集中。它引领安全和有益的AGI研究,并在全球促进采用。OpenAI在AI领域保持技术领导地位,与全球机构合作,解决AGI的挑战。它力求领导一个协作的全球努力,为集体利益开发AGI。

    • 评估摘要2 / eval_summary_2(系统生成)
      OpenAI旨在确保AGI供所有人使用,完全避免有害内容或权力过度集中。致力于研究AGI的安全性,并在AI社区中促进这些研究。OpenAI希望在AI领域处于领先地位,并与全球研究和政策团体合作,探讨AGI的相关问题。

    5. 使用ROUGE进行评估

    ROUGE是Recall-Oriented Understudy for Gisting Evaluation的缩写,主要用于衡量生成的输出与参考文本之间的词语重叠。这是评估自动文摘任务的一种流行指标。在其各种变体中,ROUGE-L提供了系统生成和参考摘要之间最长连续匹配的信息,衡量系统保留原始摘要要点的程度。

    # 计算ROUGE分数的函数
    def get_rouge_scores(text1, text2):
        rouge = Rouge()
        return rouge.get_scores(text1, text2)
    
    # 计算两个摘要与参考摘要之间的ROUGE分数
    eval_1_rouge = get_rouge_scores(eval_summary_1, ref_summary)
    eval_2_rouge = get_rouge_scores(eval_summary_2, ref_summary)
    
    rouge_scores_out = []
    
    for metric in ["rouge-1", "rouge-2", "rouge-l"]:
        for label in ["F-Score"]:
            eval_1_score = eval_1_rouge[0][metric][label[0].lower()]
            eval_2_score = eval_2_rouge[0][metric][label[0].lower()]
    
            row = {
                "Metric": f"{metric} ({label})",
                "Summary 1": eval_1_score,
                "Summary 2": eval_2_score,
            }
            rouge_scores_out.append(row)
    
    # 展示ROUGE分数
    rouge_scores_out_df = (
        pd.DataFrame(rouge_scores_out)
        .set_index("Metric")
    )
    
    rouge_scores_out_df

    上述代码计算了两个不同摘要与参考文本之间的ROUGE分数。在rouge-1方面,摘要2优于摘要1,表示单词重叠较多。而在rouge-l方面,摘要2得分较高,意味着最长公共子序列匹配较好,因此可能更好地捕捉了原始文本的主要内容和顺序。由于摘要2直接提取了摘录中的许多词汇和短语,因此与参考摘要的重叠可能较高,从而导致较高的ROUGE分数。

    然而,需要注意的是,尽管ROUGE和类似的指标(如BLEU和METEOR)提供了定量的度量,但它们通常无法捕捉良好生成摘要的真正本质。它们与人类评分的相关性也较差。鉴于LLM的进展,它们擅长生成流畅和连贯的摘要,传统的指标如ROUGE可能会误判这些模型,特别是如果摘要的表达方式不同但仍然准确地包括核心信息。

    6. 使用BERTScore进行评估

    ROUGE依赖于候选文本和参考文本中单词的精确匹配,无法解释底层语义。这就是BERTScore的作用,它利用BERT模型的上下文嵌入,旨在评估在机器生成的文本背景下,预测文本和参考句子之间的相似性。通过比较两者的嵌入,BERTScore捕获了传统n-gram指标可能忽略的语义相似性。

    # 实例化英语语言的BERTScorer对象
    scorer = BERTScorer(lang="en")
    
    # 计算摘要1与摘录的BERTScore
    P1, R1, F1_1 = scorer.score([eval_summary_1], [ref_summary])
    
    # 计算摘要2与摘录的BERTScore
    P2, R2, F2_2 = scorer.score([eval_summary_2], [ref_summary])
    
    print("摘要1 F1分数:", F1_1.tolist()[0])
    print("摘要2 F1分数:", F2_2.tolist()[0])

    上述代码计算了两个摘要与摘录之间的BERTScore分数。F1分数接近表明它们在捕获关键信息方面表现相似。然而,这种小差异应谨慎解释。由于BERTScore可能无法完全理解人类评估员可能理解的微妙和高级概念,仅依赖于这一指标可能会导致对摘要的实际质量和细微差别进行错误解读。将BERTScore与人工评估和其他指标结合使用可以提供更可靠的评估。

    7. 使用GPT-4进行评估

    在这里,我们实现了一个使用gpt-4的示例无参考文本评估器,受到G-Eval框架的启发,该框架使用大型语言模型评估生成文本的质量。与ROUGE或BERTScore等依赖于与

    参考摘要的比较的指标不同,基于gpt-4的评估器仅基于输入提示和文本评估生成内容的质量,而无需任何基准参考。这使其适用于新数据集和任务,其中人类参考文献稀缺或不可用。

    以下是此方法的概述:

    • 我们定义了四个不同的标准:

      • 相关性:评估摘要是否仅包含重要信息并排除多余信息。
      • 连贯性:评估摘要的逻辑流程和组织结构。
      • 一致性:检查摘要是否与源文档中的事实一致。
      • 流畅性:评估摘要的语法、拼写、标点、词汇选择和句子结构。
    • 我们为每个标准创建提示,以原始文档和摘要作为输入,并利用思维链生成和引导模型以输出每个标准的1-5之间的数值评分。

    • 我们使用定义的提示从gpt-4生成分数,然后比较它们在不同摘要之间。

    # 评估提示模板基于G-Eval
    EVALUATION_PROMPT_TEMPLATE = """
    您将获得一份为一篇文章写的摘要。您的任务是根据一个度量标准对摘要进行评分。
    请确保您非常仔细地阅读和理解这些说明。
    请在审阅时保持这个文档打开,并根据需要参考它。
    
    评估标准:
    
    {criteria}
    
    评估步骤:
    
    {steps}
    
    示例:
    
    源文本:
    
    {document}
    
    摘要:
    
    {summary}
    
    评估表单(仅分数):
    
    - {metric_name}
    """
    
    # 标准1:相关性
    RELEVANCY_SCORE_CRITERIA = """
    相关性(1-5) - 从源文本中选择重要内容。 \
    摘要应仅包含源文档中的重要信息。 \
    评估员被要求惩罚包含冗余和多余信息的摘要。
    """
    
    RELEVANCY_SCORE_STEPS = """
    1. 仔细阅读摘要和源文档。
    2. 比较摘要与源文档,并确定文章的主要要点。
    3. 评估摘要多好地涵盖了文章的主要要点,以及它包含了多少无关或多余的信息。
    4. 指定一个从1到5的相关性评分。
    """
    
    # 标准2:连贯性
    COHERENCE_SCORE_CRITERIA = """
    连贯性(1-5) - 所有句子的综合质量。 \
    我们将此维度与DUC质量问题中的结构和连贯性一致,即“摘要应该具有良好的结构和组织。 \
    摘要不应仅仅是一堆相关信息,而应该从句子到一\
    个关于一个主题的连贯信息体中逐渐构建起来。”
    """
    
    COHERENCE_SCORE_STEPS = """
    1. 仔细阅读文章,确定主要主题和要点。
    2. 仔细阅读摘要,并将其与文章进行比较。检查摘要是否涵盖了文章的主要主题和要点,
       并且是否以清晰且逻辑的顺序呈现它们。
    3. 根据评估标准,为连贯性评分,评分范围从1到5,其中1是最低的,5是最高的。
    """
    
    # 标准3:一致性
    CONSISTENCY_SCORE_CRITERIA = """
    一致性(1-5) - 摘要与被总结源文之间的事实一致性。 \
    一个事实一致的摘要仅包含源文档支持的陈述。 \
    评估员还被要求惩罚包含虚构事实的摘要。
    """
    
    CONSISTENCY_SCORE_STEPS = """
    1. 仔细阅读文章,确定它呈现的主要事实和细节。
    2. 仔细阅读摘要,并将其与文章进行比较。检查摘要是否包含文章不支持的任何事实错误。
    3. 根据评估标准,为一致性评分。
    """
    
    # 标准4:流畅性
    FLUENCY_SCORE_CRITERIA = """
    流畅性(1-3): 摘要的质量,涉及语法、拼写、标点、词汇选择和句子结构。
    1: 差。摘要有许多错误,使其难以理解或听起来不自然。
    2: 一般。摘要有一些错误,影响了文本的清晰度或流畅性,但主要要点仍然可以理解。
    3: 良好。摘要几乎没有错误,阅读和理解起来很容易。
    """
    
    FLUENCY_SCORE_STEPS = """
    阅读摘要并根据给定的标准评估其流畅性。指定一个从1到3的流畅性评分。
    """
    
    # 为不同标准准备数据
    evaluation_metrics = {
        "相关性": (RELEVANCY_SCORE_CRITERIA, RELEVANCY_SCORE_STEPS),
        "连贯性": (COHERENCE_SCORE_CRITERIA, COHERENCE_SCORE_STEPS),
        "一致性": (CONSISTENCY_SCORE_CRITERIA, CONSISTENCY_SCORE_STEPS),
        "流畅性": (FLUENCY_SCORE_CRITERIA, FLUENCY_SCORE_STEPS),
    }
    
    summaries = {"摘要1": eval_summary_1, "摘要2": eval_summary_2}
    
    data = {"评估
    
    标准": [], "摘要": [], "评分": []}
    
    for metric, (criteria, steps) in evaluation_metrics.items():
        for summary_name, summary_text in summaries.items():
            prompt = EVALUATION_PROMPT_TEMPLATE.format(
                criteria=criteria, steps=steps, document=excerpt, summary=summary_text, metric_name=metric
            )
            response = openai.Completion.create(
                engine="text-davinci-003", prompt=prompt, max_tokens=100
            )
            rating = int(re.search(r"\d", response.choices[0].text).group())
            data["评估标准"].append(metric)
            data["摘要"].append(summary_name)
            data["评分"].append(rating)
    
    # 将数据转换为DataFrame以进行比较
    evaluation_df = pd.DataFrame(data)
    
    # 输出评估结果
    evaluation_df

    上述代码使用gpt-4基于定义的四个标准为两个摘要生成了1-5之间的分数。这些标准包括相关性、连贯性、一致性和流畅性。根据每个标准的提示,gpt-4生成了评分,然后将这些评分与不同摘要进行比较。根据评估结果,您可以看到每个摘要在不同标准下的表现如何。

    8. 总结

    自动文摘质量评估是一个复杂的任务,涉及多个方面,包括文本的相关性、连贯性、一致性和流畅性。传统的评估指标如ROUGE和BERTScore提供了一种方式来量化自动生成的文本与参考文本之间的相似性,但它们不能全面捕捉文本质量的各个方面。因此,将它们与基于大型语言模型的评估器结合使用可能会更全面地评估文本质量。

    在这个教程中,我们展示了如何使用ROUGE、BERTScore和基于gpt-4的评估器来评估自动生成的文本的质量。请记住,这些指标和方法只是评估文本质量的一部分,最终的评估可能需要人工评估和用户反馈来全面评估文本生成系统的性能。希望这个指南能帮助您更好地了解如何评估自动文摘质量,从而改进和优化您的自动生成文本的系统。

  • 提高Spring Boot应用的并发处理能力:解决A服务中的瓶颈问题

    当处理 Spring Boot 应用程序的并发问题时,您通常需要考虑如何提高应用程序的性能,以处理多个并发请求。在某些情况下,您的应用程序可能需要与外部服务通信,这可能会成为性能瓶颈。本教程将介绍一些方法,以帮助您提高 Spring Boot 应用程序的并发处理能力。

    开篇故事

    假设您的公司有一个小程序,最初是直接调用服务 B 的接口。然而,现在的情况是小程序通过调用服务 A 的接口,而服务 A 又需要调用服务 B 来获取某些数据(例如,用于构造调用 B 服务所需的令牌)。您的任务是重构服务 A,以提高其并发处理能力,以便更好地应对小程序的需求。

    提高 Spring Boot 应用程序的并发处理能力

    以下是一些方法,可以帮助您提高 Spring Boot 应用程序的并发处理能力:

    1. 引入缓存

    您可以使用缓存来存储来自服务 B 的令牌,避免每次请求都需要调用 B 服务接口来获取令牌。您可以选择使用内存缓存或分布式缓存,具体选择取决于您的需求。

    // 示例:使用 Spring Cache 注解配置缓存
    @Cacheable("tokenCache")
    public String getToken() {
        // 获取令牌的逻辑
    }

    2. 异步处理

    将服务 A 中调用服务 B 的逻辑改为异步处理。可以使用消息队列或异步任务来实现。这样,当小程序调用 A 服务的接口时,A 服务将请求放入消息队列或异步任务中,然后立即返回响应给小程序,无需等待 B 服务的返回结果。

    // 示例:使用 Spring 的 @Async 注解实现异步方法
    @Async
    public void callServiceBAsync() {
        // 调用服务 B 的逻辑
    }

    3. 并发请求

    可以使用多线程、线程池或异步框架来实现并发请求,以提高处理能力。这样,您可以同时调用 B 服务的接口,加快处理速度。

    // 示例:使用 Java 线程池实现并发请求
    ExecutorService executorService = Executors.newFixedThreadPool(10);
    
    List<Future<?>> futures = new ArrayList<>();
    
    for (int i = 0; i < 10; i++) {
        futures.add(executorService.submit(() -> {
            // 并发调用服务 B 的逻辑
        }));
    }
    
    // 等待所有任务完成
    for (Future<?> future : futures) {
        future.get();
    }
    
    executorService.shutdown();

    4. 服务拆分

    如果服务 A 的并发处理能力仍然不足,可以考虑将 A 服务拆分成多个微服务,每个微服务负责不同的功能模块。这样可以将负载分散到多个服务上,提高整体的并发处理能力。

    5. 优化代码

    对服务 A 的代码进行优化,提高代码的执行效率。可以通过优化算法、减少不必要的计算、减少数据库查询等方式来提高代码的性能。

    // 示例:代码优化,减少数据库查询次数
    public String getData() {
        if (cachedData != null) {
            return cachedData;
        } else {
            // 查询数据库并缓存结果
            cachedData = databaseService.getDataFromDatabase();
            return cachedData;
        }
    }

    以上是一些提高 Spring Boot 应用程序并发处理能力的建议。根据您的具体情况和需求,选择合适的方法来重构您的应用程序,以更好地应对并发请求。

  • 如何使用Chat模型进行微调:一个食谱命名实体识别教程

    在数字时代,人工智能技术正不断演进,为我们的生活和工作带来了革命性的变化。开放AI(OpenAI)的GPT-3.5-turbo模型是一项重要的技术,它可以用于各种自然语言处理任务,包括聊天和命名实体识别(NER)。本教程将向您展示如何使用GPT-3.5-turbo进行微调,以执行食谱命名实体识别任务。

    1. 引子:解锁人工智能的潜力

    在当今的数字世界中,人工智能技术正日益成熟和普及。无论是自动化客户支持,还是从大规模数据中提取有用信息,AI都发挥着关键作用。而GPT-3.5-turbo模型是一种强大的AI工具,可以用于许多自然语言处理任务,使您能够创建智能聊天机器人,或者像本教程一样执行命名实体识别任务。

    2. 准备工作:设置环境

    在开始微调之前,确保您已经安装了最新版本的OpenAI Python包,并已获取了OpenAI API密钥。这个密钥将用于与GPT-3.5-turbo模型进行通信。

    # 安装最新版本的OpenAI Python包
    !pip install --upgrade openai
    
    import json
    import openai
    import os
    import pandas as pd
    from pprint import pprint
    
    # 添加您的OpenAI API密钥
    OPENAI_API_KEY = os.getenv("OPENAI_API_KEY", "")

    3. 数据准备:准备数据进行微调

    在进行微调之前,您需要准备训练数据。在本教程中,我们将使用食谱数据集,并从中提取通用的食材名称作为命名实体。以下是数据准备的步骤:

    3.1 数据集加载和筛选

    为了进行微调,我们需要一个特定领域的数据集。数据集应该足够专注,以便模型可以学习,但也要足够通用,以便不会错过未见过的示例。在本教程中,我们从RecipesNLG数据集中提取了一个子集,该子集只包含来自www.cookbooks.com的文档。

    # 读取我们将在此任务中使用的数据集
    recipe_df = pd.read_csv("data/cookbook_recipes_nlg_10k.csv")
    
    # 显示数据集的前几行
    recipe_df.head()

    3.2 数据格式转换

    在微调时,我们需要将数据转换为适用于ChatCompletion格式的训练示例。每个训练示例都是一个包含多个消息的对话列表。以下是数据转换的示例代码:

    training_data = []
    
    system_message = "You are a helpful recipe assistant. You are to extract the generic ingredients from each of the recipes provided."
    
    def create_user_message(row):
        return f"""Title: {row['title']}\n\nIngredients: {row['ingredients']}\n\nGeneric ingredients: """
    
    def prepare_example_conversation(row):
        messages = []
        messages.append({"role": "system", "content": system_message})
    
        user_message = create_user_message(row)
        messages.append({"role": "user", "content": user_message})
    
        messages.append({"role": "assistant", "content": row["NER"]})
    
        return {"messages": messages}
    
    # 对数据集的每一行应用数据转换函数
    training_data = recipe_df.apply(prepare_example_conversation, axis=1).tolist()

    3.3 上传文件

    将转换后的数据保存为.jsonl文件,然后上传到OpenAI的Files端点,以供微调使用。

    def write_jsonl(data_list: list, filename: str) -> None:
        with open(filename, "w") as out:
            for ddict in data_list:
                jout = json.dumps(ddict) + "\n"
                out.write(jout)
    
    # 保存训练数据和验证数据为.jsonl文件
    training_file_name = "tmp_recipe_finetune_training.jsonl"
    write_jsonl(training_data, training_file_name)
    
    validation_file_name = "tmp_recipe_finetune_validation.jsonl"
    write_jsonl(validation_data, validation_file_name)
    
    # 使用OpenAI的Files端点上传文件
    training_response = openai.File.create(
        file=open(training_file_name, "rb"), purpose="fine-tune"
    )
    training_file_id = training_response["id"]
    
    validation_response = openai.File.create(
        file=open(validation_file_name, "rb"), purpose="fine-tune"
    )
    validation_file_id = validation_response["id"]

    4. 微调模型

    现在,我们可以使用准备好的文件创建微调任务。在创建任务时,我们需要指定训练文件、验证文件、模型名称和可选的后缀来标识模型。以下是微调模型的示例代码:

    response = openai.FineTuningJob.create(
        training_file=training_file_id,
        validation_file=validation_file_id,
        model="gpt-3.5-turbo",
        suffix="recipe-ner",
    )
    
    job_id = response["id"]

    您可以使用以下代码检查微调任务的状态:

    response = openai.FineTuningJob.retrieve(job_id)
    
    print("Job ID:", response["id"])
    print("Status:", response["status"])

    5. 跟踪微调进度

    微调任务可能需要一些时间来完成。您可以使用以下代码跟踪微调进度:

    response = openai.FineTuningJob.list_events(id=job_id, limit=50)
    
    events = response["data"]
    events.reverse()
    
    for event in events:
        print(event["message"])

    6. 微调成功!

    一旦微调任务完成,您将获得一个微调模型的ID。以下是

    如何获取微调模型的示例代码:

    response = openai.FineTuningJob.retrieve(job_id)
    fine_tuned_model_id = response["fine_tuned_model"]
    
    print("Fine-tuned model ID:", fine_tuned_model_id)

    7. 使用微调模型进行推理

    最后,您可以使用微调的模型来执行推理。只需调用ChatCompletions并指定微调模型的名称和消息列表。以下是如何使用微调模型进行推理的示例代码:

    test_df = recipe_df.loc[201:300]
    test_row = test_df.iloc[0]
    test_messages = []
    test_messages.append({"role": "system", "content": system_message})
    user_message = create_user_message(test_row)
    test_messages.append({"role": "user", "content": create_user_message(test_row)})
    
    response = openai.ChatCompletion.create(
        model=fine_tuned_model_id, messages=test_messages, temperature=0, max_tokens=500
    )
    print(response["choices"][0]["message"]["content"])

    8. 结论

    通过本教程,您已经学会了如何使用GPT-3.5-turbo模型进行微调,以执行食谱命名实体识别任务。这个过程涵盖了数据准备、模型微调和推理的所有关键步骤。希望这个教程对您在自然语言处理领域的工作和项目中有所帮助。

  • Linux Kernel 6.5发布:初步支持Wi-Fi 7和USB4

    是时候迎来新的Linux内核发布了!

    Linux 6.5内核已经发布,它在之前的Linux 6.4内核基础上进行了改进,并引入了一些显著的变化和新增功能。

    Linus Torvalds提到这次发布非常顺利:

    过去的一周没有发生特别奇怪或可怕的事情,因此没有理由推迟6.5版本的发布。

    我仍然有一种烦人的感觉,认为很多人都在度假,事情之所以安静,部分原因是因为这个。但这次发布进展顺利,所以这可能只是我多疑。上周最大的补丁只是针对我们自己的自测。

    ? Linux Kernel 6.5:有什么新特性?
    用户应该注意,这是一个非LTS版本。如果你想要尝试最新功能,那么这个版本适合你。否则,升级是不必要的,除非它修复了特定问题或提高了性能。

    以下是新内核版本的主要亮点:

    1. 支持AMD FreeSync视频的开箱即用:在此版本中,重新引入了对AMD FreeSync视频模式的支持,并默认启用。它早在Linux内核5.8中引入过,但由于存在错误而被撤销。启用AMD FreeSync模式后,可以通过将显示器的刷新率与显卡的帧率匹配来大大减少游戏和视频中的画面撕裂和卡顿。

    2. 默认的P-State "Active"模式适用于AMD CPU:现代AMD CPU,特别是那些使用Zen 2及以上架构的CPU,现在将使用amd-pstate作为默认的CPU性能调节驱动程序。此前,默认情况下由CPUFreq管理CPU缩放机制。现在,amd-pstate提供了三种模式:active、passive和guided autonomous。在这个版本中,默认启用了"active"模式,取代了先前Linux内核6.4中使用的guided autonomous模式。据Phoronix的一些基准测试显示,amd-pstate的性能优于CPUFreq。

    3. ASUS ROG Ally声音优化:ASUS ROG Ally是AMD Z1和Z1 Extreme SoCs驱动的Windows掌上游戏电脑的直接竞争对手。它于今年7月发布。拥有ASUS ROG Ally的Linux玩家可以期待看到该系统音频设备的补丁发布,多亏了这个提交。

    4. USB4 v2和WiFi 7的初步支持:下一代无线标准WiFi 7的工作已经进行了相当长的时间。作为一个快速的事实,WiFi 7支持6GHz频段,最大数据传输速率为23Gbps!此外,下一代USB4标准的开发工作也已经开始。USB4支持80Gbps的数据传输速率!通过WiFi的合并和WiFi 7的请求,早期支持已经集成到Linux 6.5内核中。

    ?️ 其他变更和改进
    除了主要亮点之外,还有一些需要提及的事项:

    • 修复了Intel P-State的CPU缩放问题
    • 对Btrfs的性能改进和其他存储优化
    • 对最新的Xbox控制器提供了震动支持
    • 为AMD Radeon RX 7000系列显卡提供了超频支持
    • 为AMD和英特尔图形驱动程序提供了各种改进和优化
    • 增加了更多的Rust代码转换

    官方发布公告会提供自上一次发布候选版以来的简短日志。如果你对技术细节感兴趣,可以参考更新日志。

    安装Linux Kernel 6.5
    你可以使用像Arch或Fedora这样的滚动发布的发行版轻松升级到Linux Kernel 6.5。这些发行版在发布后不久就会提供最新的内核。

    另一方面,使用Ubuntu及其衍生发行版的用户应该期待在Ubuntu 23.10中看到这个Linux内核版本的使用。通过使用Pop OS和Linux Lite等发行版,你可以更早地期待这个内核版本。

    你可以从官方网站下载最新Linux Kernel的tarball(发布后需要一些时间才能获得)。

    ? 在评论中分享你对最新内核发布的看法。

  • 如何在Ubuntu Linux上释放/boot分区的空间

    最近,我收到了一个警告,提示/boot分区几乎已满或没有剩余空间了。是的,我使用的是一个单独的/boot分区,不过现在不太多的人这样做了。

    这是我第一次看到这样的错误,让我感到困惑。现在,有几种方法可以在Ubuntu(或基于Ubuntu的发行版)上释放空间,但并不是所有方法都适用于这种情况。

    因此,我决定写一篇文章,介绍我采取的步骤,以释放/boot分区的一些空间。

    在Ubuntu上释放/boot分区的空间(如果你的/boot分区快满了)

    我建议你仔细阅读这些解决方案,并选择最适合你情况的方法。这些方法都很简单,但在生产系统上执行其中一些操作时,你需要格外谨慎。

    方法1:使用apt autoremove

    你不需要成为终端专家来执行这个操作,只需一个命令,你就可以删除未使用的内核,从而释放/boot分区的空间。

    你只需要输入以下命令:

    sudo apt autoremove

    这不仅会删除未使用的内核,还会删除你不需要的依赖项,或者不被任何已安装工具所需要的依赖项。

    输入命令后,系统将列出将要被删除的内容,你只需确认操作。如果你好奇的话,你可以仔细查看它到底删除了什么。

    你需要按下Y键以继续。

    值得注意的是,如果/boot分区剩余的空间很少并且你收到了警告,这种方法只会起作用。但是,如果/boot分区已满,APT可能根本无法工作。

    在下一种方法中,我将介绍两种不同的方式,通过这两种方式你可以使用GUI和终端来删除旧的内核,以释放空间。

    方法2:手动删除未使用的内核(如果apt autoremove无效)

    在尝试删除旧的内核以释放空间之前,你需要确定当前活动的内核,并确保你不删除它。

    要检查你的内核版本,请在终端中输入以下命令:

    uname -r

    uname命令通常用于获取Linux系统信息。这个命令显示了当前正在使用的Linux内核。

    现在,你知道了你当前的Linux内核是什么,你只需要删除与此版本不匹配的内核。你应该将它记录在某个地方,以确保你不会意外删除它。

    接下来,你可以使用终端或GUI来删除它。

    警告!

    在删除内核时要特别小心。只删除旧内核,不要删除你当前正在使用的内核,否则你的系统将会损坏。

    使用GUI工具删除旧的Linux内核

    你可以使用Synaptic软件包管理器或类似Stacer的工具来开始操作。在我遇到/boot分区已满且apt不可用的情况下,我使用Stacer来删除旧的内核。让我来演示一下它是如何工作的。

    首先,你需要启动“Stacer”,然后按照下面的截图所示导航到软件包卸载器。

    在这里,搜索“image”,你将找到你拥有的Linux内核的图像。你只需要删除旧的内核版本,而不是你当前的内核镜像。

    在上面的截图中,我标出了我的当前内核和旧的内核,所以你必须小心你系统上的内核版本。

    你不需要删除其他任何东西,只需删除旧的内核版本。

    类似地,在软件包列表中搜索“headers”,并删除如下所示的旧内核。

    只是提醒一下,你不想删除“linux-headers-generic”。只关注带有版本号的那些。

    就是这样,你完成了,apt将再次正常工作,你已成功从/boot分区中释放了一些空间。同样,你可以使用任何其他你熟悉的软件包管理器来执行此操作。

    使用命令行删除旧的内核

    这与使用终端是一样的。因此,如果你没有使用GUI的选项(如果是远程机器/服务器),或者如果你只是习惯使用终端,那么你可以按照下面的步骤操作。

    首先,使用以下命令列出所有已安装的内核:

    ls -l /boot

    那些被标记为“old”的或与你当前的内核版本不匹配的内核,就是你可以删除的未使用内核。

    现在,你可以使用rm命令来从/boot分区删除特定的内核,使用以下命令(每个内核一个命令):

    sudo rm /boot/vmlinuz-5.4.0-7634
    
    -generic

    请确保检查你系统的版本 – 对于你的系统可能会有所不同。

    如果你有很多未使用的内核,这将需要时间。因此,你也可以使用以下命令来删除多个内核:

    sudo rm /boot/*-5.4.0-{7634}-*

    澄清一下,你需要用逗号分隔的方式写出内核版本的最后部分/代码,以便一次删除所有内核。

    假设我有两个旧内核5.4.0-7634-generic和5.4.0-7624,那么命令将是:

    sudo rm /boot/*-5.4.0-{7634,7624}-*

    如果你不想在grub引导菜单中看到旧的内核版本,你可以使用以下命令来更新grub:

    sudo update-grub

    就是这样。你完成了。你已经释放了空间,如果/boot分区已满并且引起了APT的问题,你也可能已经解决了这个问题。

    在某些情况下,你可能需要输入以下命令来修复损坏的apt(我在论坛上看到一些人这样做):

    sudo dpkg --configure -a
    sudo apt install -f

    请注意,除非你发现APT损坏,否则不需要输入上述命令。就我个人而言,我不需要这些命令,但我发现它们对一些人来说在论坛上非常有用。

    故障排除
    如果你在执行这些操作时遇到问题,请在评论中告诉我,我将尽力提供帮助。

    这就是如何在Ubuntu Linux上释放/boot分区的空间。如果你的系统提示/boot分区没有剩余空间,这些方法应该可以帮助你解决问题。

  • 让你的Windows系统准备好运行虚拟机

    你是否曾经遇到过在使用VirtualBox时出现“无法打开虚拟机会话”的错误?这可能是因为你的系统没有启用虚拟化技术。

    为了在Windows系统上创建虚拟机,你需要确保系统已经做好了一些准备工作。在本篇文章中,我将为你介绍如何准备你的Windows系统以运行虚拟机。

    开篇故事

    想象一下这个场景:你正准备在Windows系统上运行一个重要的虚拟机,但突然出现了一个让你束手无策的错误信息。这是每个使用虚拟机的人都可能遇到的问题。但是不要担心,我将向你展示如何准备你的Windows系统,以避免这些问题的发生,并顺利运行虚拟机。

    第一步:启用虚拟化技术

    如果你使用的是预装了Windows 10/11的系统,那么很可能已经启用了虚拟化技术,你无需进行任何设置。但如果你手动安装了Windows操作系统,那么你需要检查BIOS设置,看看是否支持虚拟化技术。

    虚拟化技术的启用对于虚拟机程序的正常运行至关重要。如果未启用虚拟化技术,虚拟机程序将无法工作并显示错误信息。

    以下是检查虚拟化技术是否启用的基本步骤:

    1. 进入UEFI固件设置(或BIOS菜单)。通常,你可以通过按下“Del”键或F1、F2、F10或F12键来访问它。

      根据主板制造商的不同,用户界面可能会有所不同。但在大多数情况下,你需要导航到其中的“高级”选项,并访问“CPU配置”设置。

    2. 在CPU配置中,你需要启用“Intel (VMX)虚拟化技术”或“SVM模式”(适用于AMD处理器)。

    启用虚拟化技术后,你就可以顺利使用虚拟机程序了。

    第二步:选择虚拟机程序

    要创建和管理虚拟机,你需要选择一个虚拟机程序。在Windows系统中,有两个主要选择:Hyper-V和第三方虚拟机程序。

    Hyper-V

    Hyper-V是Microsoft内置的虚拟机程序,如果你使用的是Windows Pro/Education/Enterprise版本,那么可以很容易地启用它。以下是启用Hyper-V的步骤:

    1. 在控制面板中搜索“Windows功能”或通过“控制面板” -> “程序” -> “打开或关闭Windows功能”来找到它。

    2. 点击“Hyper-V”并点击“确定”。

    3. 系统将自动应用更改并获取所需的文件。你只需要等待一段时间。

    4. 完成后,系统会要求你重新启动以生效新功能。

    请注意,Hyper-V不适用于Windows 10/11家庭版。

    第三方虚拟机程序

    虽然使用Hyper-V可以获得更好的虚拟机性能,但它不太容易使用。因此,我们建议普通用户使用第三方虚拟机程序。

    其中,VirtualBox是一个不错的选择。它是一个开源程序,具有丰富的功能和用户友好的界面。你可以在Windows、Linux和macOS上使用它。

    你还可以选择像VMware Workstation这样的专有选项,它也非常流行。

    第三步:检查系统资源和要求

    创建和使用虚拟机并不是非常消耗资源的过程。然而,有一些变量可能需要你关注:

    • 确保你的系统至少有4 GB的RAM(越多越好)。
    • 64位处理器,具有双核或更多核心。

    如果你不知道,虚拟机会使用你系统的资源,即使它们是独立的虚拟机。大多数最低规格建议包括4 GB的RAM,但我建议使用8 GB以上。

    如果你想运行两个虚拟机,那么在Windows上可能需要更多的RAM。

    除了内存外,你应该拥有一个拥有多个核心的处理器。这样,一些核心可以用于虚拟机,另一些可以用于主机上的其他任务。

    最后,要注意磁盘空间。对于虚拟机,磁盘通常是动态分配的,这意味着随着操作系统和文件的增加,物理存储驱动器上的空间会被占用。

    在某些类型的虚拟磁盘中,它会保留你指定的整个空间。因此,在执行此操作之前,请检查启动之前的可用磁盘空间。通常情况下,选择一个不安装Windows系统的分区作为虚拟机磁盘是一个不错的选择。

    结论

    通过按照以上步骤来准备你的Windows系统,你可以轻松地运行和管理虚拟机。现在,你可以在Windows上轻松安装Linux虚拟机,开始你的虚拟化之旅。

    如果你还有其他关于虚拟机的问题或想要了解更多信息,欢迎在下面的评论中与我们分享你的想法。

  • 如何使用Python优化爬虫和数据可视化

    你是否曾经想过如何从Bilibili网站上获取有关UP主的信息,并将其可视化呈现出来?本教程将向你展示如何使用Python、Selenium、Pandas和Matplotlib优化你的爬虫和数据可视化流程,以更轻松地收集和分析数据。

    前言

    在这个信息爆炸的时代,我们有许多方法来获取数据。而对于喜欢Bilibili的用户来说,UP主的粉丝数量和内容质量是非常关键的信息。我们将使用Python编程语言来自动抓取Bilibili上UP主的信息,并通过数据可视化来更好地理解这些数据。

    准备工作

    在开始之前,确保你已经安装了以下Python库:

    • Pandas:用于数据处理和分析。
    • Matplotlib:用于数据可视化。
    • Selenium:用于网页自动化操作。

    你还需要下载Microsoft Edge浏览器驱动,确保与你的浏览器版本相匹配。现在,让我们开始吧!

    第一步:设置环境

    首先,让我们设置Python环境并导入必要的库:

    import pandas as pd
    import matplotlib.pyplot as plt
    from selenium import webdriver
    from selenium.webdriver.common.by import By
    import pinyin

    在这里,我们导入了所需的库,包括Pandas用于数据处理、Matplotlib用于可视化、Selenium用于网页自动化操作以及pinyin用于处理拼音。

    第二步:设置中文显示

    由于我们将处理中文字符,我们需要设置Matplotlib以正确显示中文字符:

    plt.rcParams["font.sans-serif"] = ["SimHei"]
    plt.rcParams["axes.unicode_minus"] = False

    这将确保你的可视化图表中可以正确显示中文。

    第三步:编写辅助函数

    在进行爬取和数据处理之前,我们将编写一些辅助函数来处理文字数据:

    def getStrAllAlpha(string):
        return pinyin.get_initial(string, delimiter="").upper()
    
    def getStrFirstAlpha(string):
        string = getStrAllAlpha(string)
        string = string[0:1]
        return string.upper()

    这两个函数将用于将UP主的名字转换为拼音,并提取首字母,以便后续的数据整理。

    第四步:爬取UP主信息

    现在,让我们开始爬取Bilibili上UP主的信息。我们将使用Selenium来自动化这个过程。首先,设置Bilibili的搜索页面URL:

    url = "https://search.bilibili.com/upuser?keyword=mc&from_source=webtop_search&spm_id_from=333.1007&order=fans"

    接下来,设置浏览器的User-Agent,以模拟用户操作:

    header = {'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2227.1 Safari/537.36'}
    opt = webdriver.EdgeOptions()
    opt.add_argument('--user-agent=%s' % header)
    edge = webdriver.Edge(options=opt)

    然后,让浏览器打开Bilibili的搜索页面:

    edge.get(url)

    现在,我们准备开始爬取UP主信息。我们将循环爬取100个UP主的名字和粉丝数量,并将它们存储在两个列表中:

    name_list = []
    people_list = []
    
    for i in range(1, 101):
        try:
            # 使用XPath定位UP主信息
            li = edge.find_element(by=By.XPATH, value='//*[@id="user-list"]/div[1]/ul/li[' + str(i) + ']')
            txt = li.text
            name = txt.split("\n")[0].split("+")[0]
            people = txt.split("\n")[2].split(":")[1].split("万")[0]
            name_list.append(name)
            people_list.append(float(people))
            print(name, people)
        except:
            print("切页")
            # 点击下一页按钮
            button = edge.find_element(by=By.XPATH, value='//*[@id="user-list"]/div[1]/div[2]/div/ul/li[' + str(page) + ']/button').click()
            page += 1

    这个循环将遍历100个UP主的信息,并将它们存储在两个列表中。如果需要翻页,它还会自动点击下一页按钮。

    第五步:数据整理

    现在,我们已经成功爬取了UP主的信息,接下来让我们对数据进行整理。首先,我们将按照首字母对UP主进行分类:

    abc_name = []
    abc_name2 = []
    abc_people = []
    abc_people2 = []
    abc = "A"
    
    for i in range(26):
        for j in name_list:
            if getStrFirstAlpha(j) == abc:
                abc_name.append(j)
                tt = name_list.index(j)
        abc = chr(ord(abc) + 1)
    
    for id in abc_name:
        if id not in abc_name2:
            abc_name2.append(id)
    
    for i in abc_name2:
        abc_people2.append(people_list[name_list.index(i)])

    这段代码会将UP主按照首字母进行分类,并创建两个新的列表,分别存储UP主的名字和粉丝数量。

    第六步:数据保存

    我们将整理好的数据保存为CSV文件,以备后续分析和可视化使用:

    data = pd.DataFrame({"UP主名字": name_list, "粉丝数量/万": people_list})
    data.to_csv("up主信息.csv")
    
    data = pd.DataFrame({"UP主名字": abc_name2, "粉丝数量/万": abc_people2})
    data.to_csv("按首字母分类.csv")

    这将创建两个CSV文件

    ,分别存储UP主的详细信息和按首字母分类的信息。

    第七步:数据可视化

    现在,我们已经成功获取和整理了UP主的信息,接下来让我们使用Matplotlib进行数据可视化。我们将创建两个柱状图,一个显示所有UP主的粉丝数量,另一个按首字母分类显示:

    plt.subplot(1, 2, 1)
    bar1 = plt.bar(name_list, people_list, color=['r', 'r', 'r', 'b', 'b', 'b', 'b', 'b', 'b', 'b', 'b', 'b', 'b', 'b', 'b', 'b', 'b', 'b', 'b', 'b'])
    plt.xticks(rotation=90, fontsize=13)
    plt.bar_label(bar1, label_type='edge')
    
    plt.subplot(1, 2, 2)
    bar2 = plt.bar(abc_name2, abc_people2, color=['r', 'r', 'r', 'b', 'b', 'b', 'b', 'b', 'b', 'b', 'b', 'b', 'b', 'b', 'b', 'b', 'b', 'b', 'b', 'b'])
    plt.bar_label(bar2, label_type='edge')
    plt.xticks(rotation=90, fontsize=13)
    
    plt.show()

    这段代码会创建两个子图,一个显示所有UP主的粉丝数量,另一个按首字母分类显示。最后,通过plt.show()将图表显示出来。

    总结

    通过这个教程,你学会了如何使用Python、Selenium、Pandas和Matplotlib来爬取Bilibili上UP主的信息,并将其进行数据整理和可视化。这对于分析UP主的粉丝数量和分类是非常有用的。

    现在,你可以探索更多的数据分析和可视化方法,或者将这些数据用于其他有趣的项目。希望这个教程对你有所帮助!

  • 如何创建一个自动化按键和鼠标点击工具

    在日常计算机使用中,我们常常需要进行重复性的操作,例如定时按下某个键盘按键或模拟鼠标点击。这可能涉及到玩游戏、自动化测试、数据录入等各种场景。为了实现这些功能,你可以创建一个自动化按键和鼠标点击工具。本教程将介绍如何使用Python和一些库来创建这样的工具。

    1. 开场故事

    想象一下,你正在玩一个需要不断点击鼠标的游戏,或者你需要在浏览器上定期执行某项任务。每次都手动点击或按下键盘按键是多么繁琐和乏味!那么,有没有一种方法可以自动化这些操作,让计算机来代劳呢?答案是肯定的,本文将向你展示如何创建一个自动化按键和鼠标点击的工具,让你告别重复性操作的困扰。

    2. 准备工作

    在开始之前,我们需要准备一些工具和库。首先,你需要安装Python,如果你还没有安装,可以从官方网站下载并安装。接下来,我们将使用以下库:

    • PyQt5:用于创建图形用户界面。
    • pynput:用于监听键盘和鼠标事件。
    • pygetwindow:用于获取当前活动窗口的信息。
    • pyautogui:用于模拟键盘按键和鼠标点击事件。

    你可以使用以下命令安装这些库:

    pip install PyQt5 pynput pygetwindow pyautogui

    3. 创建图形用户界面

    首先,我们将创建一个图形用户界面(GUI),以便用户可以轻松地控制自动化工具。我们将使用PyQt5库来构建GUI。以下是一个示例GUI的代码:

    from PyQt5 import QtWidgets, uic
    from PyQt5.QtGui import QPainter, QColor, QCursor
    from PyQt5.QtCore import Qt, QRectF
    from pynput.mouse import Button, Controller
    import pyautogui
    import threading
    import time
    import os
    
    # ... 其他代码 ...
    
    class Ui(QtWidgets.QMainWindow):
        def __init__(self):
            super(Ui, self).__init__()
            uic.loadUi(ui_path, self)
    
            # ... 创建GUI元素 ...
    
            self.show()
    
    # ... 其他代码 ...

    在这个GUI中,我们可以添加按键、设置时间间隔、监控按键状态等功能。

    4. 监听键盘和鼠标事件

    接下来,我们将创建一个用于监听键盘和鼠标事件的类。我们使用pynput库来实现这一功能。以下是一个示例代码片段:

    from pynput import keyboard, mouse
    import pygetwindow as gw
    
    class Controller:
        def __init__(self):
            self.mouse_controller = mouse.Controller()
            self.mouse_pressed = False
            self.keyboard_stop_flag = True
            self.mouse_auto_stop_flag = True
    
        def on_press(self, key):
            try:
                # 检查游戏是否运行并且是当前活动窗口
                # game_windows = gw.getWindowsWithTitle(self.game_title)
                # if game_windows and gw.getActiveWindow() in game_windows:
                if key == keyboard.Key.f1:
                    if self.keyboard_stop_flag:
                        self.keyboard_stop_flag = False
                        window.statusButton.setChecked(True)
                        self.start_keyboard_press_key()
                    else:
                        self.keyboard_stop_flag = True
                        window.statusButton.setChecked(False)
                elif key == keyboard.Key.f2:
                    # 鼠标连点
                    if self.mouse_auto_stop_flag:
                        self.mouse_auto_stop_flag = False
                        window.mouseStatusButton.setChecked(True)
                        self.start_mouse_auto_click_key(self.MOUSE_STATUS_CLICK)
                    else:
                        self.mouse_auto_stop_flag = True
                        window.mouseStatusButton.setChecked(False)
                elif key == keyboard.Key.f3:
                    # 鼠标长按
                    if self.mouse_auto_stop_flag:
                        self.mouse_auto_stop_flag = False
                        window.mouseStatusButton.setChecked(True)
                        self.start_mouse_auto_click_key(self.MOUSE_STATUS_PRESS)
                    else:
                        self.mouse_auto_stop_flag = True
                        window.mouseStatusButton.setChecked(False)
                elif key == keyboard.Key.f4:
                    # 按键映射
                    pass
                elif key == keyboard.Key.f12:
                    mouse_pos = QCursor.pos()
                    global mouseX, mouseY
                    mouseX = mouse_pos.x()
                    mouseY = mouse_pos.y()
                    window.xPos.setText(str(mouseX))
                    window.yPos.setText(str(mouseY))
                else:
                    print(f"{key} pressed")
            except Exception as e:
                import traceback
                traceback.print_exc()
    
        # ... 其他代码 ...

    在这个类中,我们定义了一个Controller类,用于监听键盘和鼠标事件。根据按键的不同,我们可以执行不同的操作,例如启动键盘按键模拟、鼠标点击等。

    5. 创建自动化操作

    现在,我们可以创建自动化的操作,例如模拟键盘按键和鼠标点击。以下是示例代码:

    class Controller:
        # ... 其他代码 ...
    
        def start_keyboard_press_key(self):
            for key, interval in key_time_dict.items():
                if key in pyautogui.KEYBOARD_KEYS:
                    print("启动线程, 按键: ", key, " 间隔: ", interval)
                    t = threading.Thread(target=self.press_key, args=(key, interval))
                    t.daemon = True
                    t.start()
                else:
                    print(f"Key: {key} is not a valid key")
    
        def press_key(self, key, interval):
            print("按键: ", key, " 间隔: ", interval)
            while True:
                if self.keyboard_stop_flag:
                    break
                pyautogui.press(key)
                time.sleep(interval)
    
        def start_mouse_auto_click_key(self, status):
            print("启动鼠标自动点击线程... ")
            if status == self.MOUSE_STATUS_CLICK:
                print(f"鼠标自动点击... delay:{window.mouseDelay.value()}")
                delay = window.mouseDelay
    
    .value()
                t = threading.Thread(target=self.mouse_click, args=(delay,))
                t.daemon = True
                t.start()
            elif status == self.MOUSE_STATUS_PRESS:
                print("鼠标长按... ")
                mouseC = mouse.Controller()
                pyautogui.mouseDown()
                t = threading.Thread(target=self.mouse_press)
                t.daemon = True
                t.start()
    
        def mouse_click(self, interval):
            print("鼠标自动点击, 间隔: ", interval)
            while True:
                if self.mouse_auto_stop_flag:
                    break
                # 获取鼠标当前位置
                print("指定的坐标... {}, {}".format(mouseX, mouseY))
                if mouseX == 0 and mouseY == 0:
                    mouseXx, mouseYy = pyautogui.position()
                else:
                    mouseXx = mouseX
                    mouseYy = mouseY
                pyautogui.click(int(mouseXx), int(mouseYy))
                time.sleep(interval)
    
        def mouse_press(self):
            print("开启鼠标长按... ")
            while True:
                if self.mouse_auto_stop_flag:
                    # 释放鼠标左键并退出循环
                    pyautogui.mouseUp()
                    print("鼠标长按结束")
                    break
                print("鼠标长按中... ")
                time.sleep(0.3)

    这个部分的代码用于启动键盘按键模拟和鼠标点击操作。根据用户的设置,它可以实现自动点击、长按和模拟按键等功能。

    6. 启动监听器

    最后,我们需要启动监听器来监控键盘和鼠标事件。我们在主程序中创建了一个start_listener函数,该函数启动了监听键盘和鼠标事件的线程。以下是示例代码:

    def start_listener():
        game_title = "Your Game Title"  # 替换为你的游戏窗口标题
        global controller
        controller = Controller()
        # 启动一个守护线程
        print("启动线程监听...")
        daemon = threading.Thread(target=keyboard_listener, args=(controller,))
        daemon.daemon = True
        daemon.start()
        daemon = threading.Thread(target=mouse_listener, args=(controller,))
        daemon.daemon = True
        daemon.start()
    
    def keyboard_listener(args):
        print("启动线程监听键盘...")
        controller = args
        with keyboard.Listener(
                on_press=controller.on_press,
                on_release=controller.on_release) as listener:
            listener.join()
    
    def mouse_listener(args):
        print("启动线程监听鼠标...")
        controller = args
        with mouse.Listener(
                on_click=controller.on_mouse_press,
                on_release=controller.on_mouse_release) as listener:
            listener.join()

    通过这些代码,我们可以启动监听器来监控键盘和鼠标事件,然后根据用户的设置执行相应的自动化操作。

    7. 总结

    通过创建一个自动化按键和鼠标点击工具,你可以轻松地执行重复性的操作,提高工作效率。这个工具可以用于各种应用场景,包括游戏、自动化测试、数据录入等。希望本教程对你有所帮助,让你更好地掌握自动化操作的技巧。

  • 如何刷机并安装OpenWrt系统到360 T7路由器

    嗨,各位技术达人!你是否曾想过将你的360 T7路由器升级到更强大的OpenWrt系统,以获得更多的功能和性能?如果是的话,你来对地方了。本文将为你详细介绍如何刷机并安装OpenWrt系统到你的360 T7路由器,让你充分释放这个性价比极高的设备的潜力。

    第一步:准备工作

    在开始刷机之前,确保你已经准备好以下材料和条件:

    • 一台360 T7路由器
    • 一台运行Windows的电脑(需要有以太网插口)
    • USB转TTL模块(可以在淘宝等平台购买)
    • 一根公对母的杜邦线(用于连接TTL模块和路由器)
    • 下载好的OpenWrt固件

    第二步:开启Telnet

    开始之前,我们需要将路由器进入Telnet模式,这需要一些步骤:

    1. 打开360 T7路由器的底部,你会看到两颗螺丝,用螺丝刀将它们拧下来。注意:底盖可能会有很多卡扣,需要用力打开。
    2. 打开底部后,你会看到TTL串口,上面有RXD、TXD、GND三个引脚。使用杜邦线将这些引脚连接到TTL模块(路由器的RXD连接到模块的TXD,路由器的TXD连接到模块的RXD,GND连接到GND)。
    3. 使用串口调试助手(如Termius)连接到TTL模块,选择正确的COM口和波特率115200,然后连接。

    现在,你将能够看到路由器的开机日志输出。接下来,我们将进入Failsafe模式,输入"f"(需要回车换行)并观察日志,直到看到成功进入Failsafe模式的信息。

    第三步:刷入U-Boot

    在Failsafe模式下,你可以输入以下命令来刷入U-Boot:

    fw_setenv bootmenu_delay 3
    mount_root
    sed -i 's/.*local debug=.*/\tlocal debug=1/' /etc/init.d/telnet
    passwd root
    # 输入两次相同的密码
    password
    password
    reboot

    这些命令的作用是开启U-Boot控制台菜单、挂载rootfs并开启Telnet,然后修改root密码并重启路由器。

    第四步:刷入U-Boot并安装OpenWrt

    重启后,你将能够正常配置你的路由器。现在,你可以通过Telnet连接到路由器的U-Boot界面,并使用以下命令来刷入U-Boot并安装OpenWrt:

    cd /tmp
    wget https://sebs.oss-cn-shanghai.aliyuncs.com/360t7-fip-fixed-parts.bin
    mtd write 360t7-fip-fixed-parts.bin fip

    刷入U-Boot后,路由器将自动重启并进入新的OpenWrt固件。你可以重新安装好底盖并开始享受OpenWrt系统的强大功能。

    第五步:配置OpenWrt

    要配置OpenWrt系统,你可以通过浏览器访问默认的后台地址:192.168.6.1。默认的用户名是root,密码是你之前设置的密码(在刷U-Boot时设置)。

    现在,你可以自由配置OpenWrt系统,包括Wi-Fi设置、网络配置、安全性等。

    性能测试

    你的360 T7现在已经刷入了OpenWrt系统,让我们看看它的性能表现:

    • CoreMark跑分:8803.7,显示CPU性能较好。
    • 科学性能:在合适的设置下,能够达到800Mbps以上的性能,这将满足大多数家庭的需求。
    • HWNAT硬件加速:能够轻松达到1000Mbps宽带,而CPU几乎不占用。

    无线信号也非常强大,能够在近距离和隔墙距离下提供快速稳定的连接。

    结语

    通过刷机并安装OpenWrt系统,你的360 T7路由器将获得更多功能和更强大的性能,让你充分发挥这个性价比极高的设备的潜力。虽然刷机需要一些技术操作,但遵循正确的步骤,你将能够成功完成并享受更好的路由器性能。

    希望这篇教程对你有所帮助。如果你有任何问题或需要更多帮助,请随时留言。

  • 解锁Google开发者账号:应对关联下架和封号的有效方法

    有一天,你突然发现自己的Google开发者账号被关联下架或封号了,这是许多开发者不愿面对的噩梦。Google Play作为全球最大的应用商店之一,确实为开发者提供了巨大的机会,但也伴随着严格的审查机制和风险。本文将探讨为什么你的开发者账号可能会被移除,并分享如何避免关联下架和封号的有效方法。

    第一步:上架流程

    首先,让我们回顾一下Google Play的上架流程。在这一步,你需要确保你的账号和应用信息是绝对干净的。这包括手机号、银行卡、网络环境等都不应该有关联。

    st=>start: 开始
    op1=>operation: 确保账号干净
    op2=>operation: 提交应用
    op3=>operation: 等待审查
    e=>end: 完成上架
    
    st->op1->op2->op3->e

    在这个过程中,你必须确保你的账号和应用信息都是绝对的清白,没有任何关联。

    第二步:关联下架问题

    关联下架是许多Google开发者都会面临的问题。你可能会收到类似这样的邮件:

    这封邮件通常是关联下架的模板邮件,一旦你的账号被关联,就会被封号下架,而且你可能无法得知具体的下架原因或提出申诉。

    为什么会被关联下架?

    账户关联是指Google Play认为你的多个应用可能是相似的或违反了开发者政策的情况。这可能是因为你的应用存在马甲包的情况,一旦被发现,就会导致所有应用被下架和封号。

    如果你只有一个应用上架,但出现了关联下架的情况,那么问题可能在于你的账号本身不够干净。在这种情况下,注册一个全新的开发者账号可能是最稳妥的方法。

    第三步:如何保证账号干净?

    要确保账号干净,你需要注意以下几个方面:

    1. 隐私链接:隐私链接要使用与违规账号不同的域名,并确保隐私链接内的联系方式与之前无关。

    2. 账号密码和辅助邮箱:每个开发者账号都应该有不同的密码,辅助邮箱也不应与违规账号有关。

    3. 素材图和宣传文案:商店素材图和宣传文案应定期更换,确保不要相似度太高。

    4. 应用名称/LOGO/UI:如果你的应用曾多次下架,务必更换应用名称、LOGO、UI等素材,避免与违规应用相似度过高。

    5. 后端接口:更换服务器的IP地址和域名,改变接口结构,加密数据传输,以防被关联。

    6. 客户端代码:代码混淆是确保客户端代码不容易被关联的关键。混淆的程度越高越好,确保代码结构、类名、方法名、变量名等都具有唯一性。

    st=>start: 开始
    op1=>operation: 更换隐私链接
    op2=>operation: 修改密码和辅助邮箱
    op3=>operation: 定期更新素材图和宣传文案
    op4=>operation: 更换应用名称/LOGO/UI
    op5=>operation: 更改后端接口
    op6=>operation: 提高代码混淆程度
    e=>end: 完成
    
    st->op1->op2->op3->op4->op5->op6->e

    这些步骤可以帮助你确保你的开发者账号和应用在Google Play上保持干净,降低被关联下架的风险。

    结论

    Google Play的上架过程是复杂的,而关联下架和封号是每个开发者都应该警惕的问题。为了避免这些问题,你需要保持账号和应用的绝对干净,定期更新关键信息,遵守Google Play的政策。谷歌的政策不断升级,我们必须与时俱进,以确保我们的应用能够长期留在平台上。